Cpp面试高频题

(1) 指针和引用的区别

引用:
1.引用初始化完成,将一直绑定一个对象,无法令引用绑定另外一个对象,这就是说引用必须初始化。
2.注意引用初始化的时候,绑定的是一个对象,而引用本身不是一个对象,所以不能定义引用的引用
3.引用的对象类型之间必须匹配

指针:
1.指针本身是一个对象,对对象的操作,对指针同样可以,例如拷贝赋值等
2.指针没有规定必须初始化,指针没有初始化,编译器会默认分配一个不确定的值。
3.引用不是对象,指针不能指向某个引用
4.指针的类型也必须匹配

(2) 堆和栈的区别

在c++中内存的分配分为堆和栈两种,其中由系统系统自动分配的是栈,由程序员主动向操作系统申请的是堆。

栈:由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。注意,const局部变量也储存在栈区内,栈区向地址减小的方向增长。即创建变量的时候,后创建变量的地址越小。

堆:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则则导致内存泄露。

堆栈的比较:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

在数据结构上,也存在有堆和栈的区别:

栈:是一种连续储存的数据结构,具有先进后出的性质。通常的操作有入栈(圧栈)、出栈和栈顶元素。想要读取栈中的某个元素,就要将其之前的所有元素出栈才能完成。

堆:是一种非连续的树形储存数据结构,每个节点有一个值,整棵树是经过排序的。特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。常用来实现优先队列,存取随意。
https://www.cnblogs.com/ChenZhongzhou/p/5685537.html

(3) new和delete是如何实现的,new 与 malloc的异同处

new/delete是c++的操作运算符,能够完成动态内存的分配和初始化工作,后者可以完成清理和释放内存的工作,并且在调用的时候,编译器会自动执行对象的构造函数和析构函数。new一个对象的时候,编译器自动调用构造函数,通过从内存堆上分配一定的空间,构造出对象,当调用delelte的时候调用对象的析构函数,销毁对象。

malloc/free与new/delete异同点

相同点

malloc/free与new/delete都可以用于申请动态内存和释放内存,他们申请的空间都在堆上分配。

不同点

1)操作对象不同
malloc/free是C++/C语言的标准库文件,new/delete是C++的运算符;
对非内部数据对象,malloc/free无法满足动态对象要求。对象在创建时要自动执行构造函数,对象消亡之前要自动执行析构函数,而malloc/free是库函数,不是运算符,故不在编译器控制权限之内,不能够将执行构造函数和析构函数强加于malloc/free身上。而由于new/delete是C++语言,能够完成动态内存分配和初始化工作,并能够完成清理与释放内存工作,即能够自动执行构造函数和析构函数;

2)用法不同
malloc分配内存空间前需要计算分配内存大小;而new能够自动分配内存空间;
malloc是底层函数,其函数返回值类型为void *;而new运算符调用无参构造函数,故返回值为对应对象的指针;
malloc函数类型不是安全的,编译器不对其进行类型转换、类型安全的相关检查。malloc申请空间后,不会对其初始化,要单独初始化;而new类型是安全的,因为它内置了sizeof、类型转换和类型安全检查功能,且在创建对象时,就完成了初始化工作,一般初始化调用无参构造函数;

operator new对应于malloc,且operator new可以重载,可以自定义内存分配策略,甚至不做内存分配,甚至分配到非内存设备上;但malloc不能。

free只进行释放空间;而delete则释放空间的同时调用析构函数。
此外delete使用是注意释放数组的方法为delete []数组名。

联系

new和delete功能覆盖了malloc/free,但因C++程序常会用到C函数,而C函数只能使用malloc/free管理动态内存。此外,使用是malloc和free搭配使用,new和delete搭配使用,不能混乱使用。
http://blog.csdn.net/yzhang6_10/article/details/51136018

(4) C和C++的区别

C语言是面向过程的,C++是面向对象的

C++包括普通的C编程,还有面向对象的编程,同时有有泛型编程,模板编程等新功能。

从项目上讲,C语言更过的是具象,C语言更注重功能,按照功能逻辑一个个编程,而C++设计出发点就是不同的,它是一种抽象,将设计用抽象的方式表达出来。C++很明显在抽象,设计模式,封装上有C语言完全不能比的特性,这些特性在实际上项目后期的维护中会带来很大的改变。

(5) C++、Java的联系与区别,包括语言特性、垃圾回收、应用场景等(java的垃圾回收机制)

联系:

两者都是面向对象的语言,两者都能实现面向对象的核心思想(封装、继承、多态)。但是由于c++为了兼容c语言,java不兼容C,它是一种完全的面向对象语言。

区别:

语言特性:

1.指针:c++有指针来访问内存,而JAVA中对于用户态,编程者无法找到指针来直接访问内存指针,只有限定版的引用,更加安全。
2.多重继承:c++支持多重继承,可以继承多个父类。JAVA不支持多重继承,但是允许一个类继承多个接口。
3.数据类型和类:Java是完全面向对象的语言,所有函数和变量都必须是类的一部分。除了基本数据类型之外,其余的都作为类对象,包括数组。对象将数据和方法结合起来,把它们封装在类中,这样每个对象都可实现自己的特点和行为。而c++允许将函数和变量定义为全局的。
4.自动内存管理:java内存支持自动对无用内存回收管理,c++需要人为的使用delete去释放内存。
5.c++支持操作符重载,但是java不支持操作符重载

垃圾回收:

java内存支持自动对无用内存回收管理,c++需要人为的使用delete去释放内存。

应用场景:

C++相对于java来看是偏底层的语言,应用场景也是一些偏底层的软件,例如:图像,客户端,桌面软件等。
JAVA则是偏向应用的语言,相对来说,生态圈较好,有一些高级特性也比较好用,一般是上层应用软件,例如移动设备的软件,web网页后台逻辑开发等等。

(6) struct和class的区别

在c++中,使用class和struct的关键字区别是,默认的访问权限不一样,如果用struct去定义,则第一个访问说明符之前,默认的是public的,但是class,第一个访问说明符之前,默认的就是privite的区别,在定义类上这是唯一的区别。

继承:class继承默认是private继承,而struct继承默认是public继承

(7) define 和const的区别(编译阶段、安全性、内存占用等)

编译阶段:define 是预编译阶段展开,而const是在运行阶段使用

安全性:const常量是有数据类型的,那么编译器会对const变量的类型等安全性进行检查,但是define只是在预编译阶段展开,不会进行类型的安全检查,替换时可能产生安全错误。

内存占用:define不会占用内存,单纯的替换而已,const会占用内存,会有对应的内存地址。

(8) 在C++中const和static的用法(定义,用途)

定义:

在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。

static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate=2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static。

用途

cosnt成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,但可以访问成员变量。当方法成员函数时,该函数只能是const成员函数。

static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有this指针,这导致:1、不能直接存取类的非静态成员变量,调用非静态成员函数2、不能被声明为virtual.
https://www.cnblogs.com/phpzhou/p/6390869.html

(9) const和static在类中使用的注意事项(定义、初始化和使用)

定义:

const可以在类内部定义,但是定义的位置不能初始化;static只能在类内部声明,定义只能在类的外面,并且定义的时候不能加static关键字

初始化:

const不能再定义的位置初始化,只能在类的构造函数的初始化列表中初始化;static初始化不能再类的内部进行初始化,必须在外部定义的时候初始化。

使用:

const的使用主要目的是防止成员函数修改对象的内容,即const成员函数不能修改成员变量的值,但可以访问成员变量。
static的使用目的是作为类作用域的全局函数。不能访问类的非静态数据成员,类的静态成员函数没有this指针,这导致不能直接存取类的非静态成员变量,调用非静态成员函数,不能声明为virtual.

https://www.cnblogs.com/phpzhou/p/6390869.html

(10) C++中的const类成员函数(用法和意义)

const类成员函数有这么几种:

1.void fun() const;
表明是常量成员函数,这个const表明了该函数不会改变任何成员数据的值。
2.void fun(const a) const;
表明是参数是常量的常量成员函数,接收的参数是常量,同时不能改变成员数据的值。

意义:为什么要这么做?

这是为了保证它能被const常量对象调用,我们都知道,在定义一个对象或者一个变量时,如果在类型前加一个const,如const int x;,则表示定义的量为一个常量,它的值不能被修改。但是创建的对象却可以调用成员函数,调用的成员函数很有可能改变对象的值。所以这个时候const类成员函数就出现了。

于是,我们把那些肯定不会修改对象的各个属性值的成员函数加上const说明符,这样,在编译时,编译器将对这些const成员函数进行检查,如果确实没有修改对象值的行为,则检验通过。以后,如果一个const常对象调用这些const成员函数的时候,编译器将会允许。
http://blog.csdn.net/u010661782/article/details/49020595

(11) 计算下面几个类的大小:

class A {};: sizeof(A) = 1;
class A { virtual Fun(){} };: sizeof(A) = 4(32位机器)/8(64位机器);
class A { static int a; };: sizeof(A) = 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };: sizeof(A) = 4;

1.确切的说,类只是一个类型定义,它是没有大小可言的。 用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。

2.一个对象的大小大于等于所有非静态成员大小的总和,大于的部分是由编译器自主添加的。
C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。 如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。而虚函数本身和其他成员函数一样,是不占用对象的空间的。

3.对象占空的内存不算成员函数的占空的内存,只算成员变量的内存。

所以:

空类,编译器为了能存放对象,给他自动分配了一个字节的地址
虚函数的A,存在有虚函数表,编译器给这个类添加了指向虚函数表的函数指针,主要这是必须的,指针的大小取决于物理地址的大小
静态变量,静态变量是是不包括在这个类其中的,所以算类的大小是没有静态成员变量的。
int,存在成员变量,相当于计算成员变量的大小,int型4个字节
同样的道理,静态变量的大小是不包括在这个类当中的。

(12) 给一个代码,求输出结果

class A
{
public:
A(int x){}
}

问:A a = 1;是否正确, 如果正确, 那么它调用了哪些函数?

这类题目更常见的是在基类和子类有不同实现方法。(虚函数相关,栗子很多,不多说了)

正确。由于A没有显示的声明,所以可以用int型进行强制转换,编译器碰到这种情况,
首先,如果是没有优化的编译器,对1进行强制转换int型,然后进行调用默认的赋值函数,1赋值给x,然后调用构造函数构造。

其次,如果是有优化的编译器,这个时候编译器可能会直接将1强制转换为A类型,调用了一次构造函数,然后在赋值给a,这个时候调用的是默认的赋值构造函数。

(13) C++的STL介绍(这个系列也很重要,建议侯捷老师的这方面的书籍与视频),其中包括内存管理allocator,函数,实现机理,多线程实现等

STL是一个c++里面非常强大的库,c11引进的,里面封装例如容器,泛型算法等。

(14) STL源码中的hash表的实现

hash_table是STL中hash_map 和 hash_set 的内部数据结构,hash_table的插入/删除/查找的时间复杂度都为O(1),是查找速度最快的一种数据结构,但是hash_table中的数据是无序的,一般也只有在数据不需要排序,只需要满足快速查找/插入/删除的时候使用hash_table。hash_table的扩展是将原hash_table中的数据摘下来插入到一个临时的hash_table中,因为每个桶都使用list来实现的,因此插入删除都不存在内存copy,所以也是很高效的,最后再将临时hash_table和原来的hash_table(此时已经为空)交换。

(15) STL中unordered_map和map的区别

map是一种映射,这种映射是有序的,底层是使用红黑树来完成的,数据通过键值才存储,键是唯一的。

unordered_map,是一种无序的,底层是通过hash表来完成的。unordered库使用“桶”来存储元素,散列值相同的被存储在一个桶里。当散列容器中有大量数据时,同一个桶里的数据也会增多,造成访问冲突,降低性能。为了提高散列容器的性能,unordered库会在插入元素是自动增加桶的数量,不需要用户指定。每个通都是用list来完成的。

map

优点:

● 有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作
● 红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高

缺点:

● 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点,孩子节点以及红/黑性质,使得每一个节点都占用大量的空间
● 适用处,对于那些有顺序要求的问题,用map会更高效一些

unordered_map

优点:

● 因为内部实现了哈希表,因此其查找速度非常的快

缺点:

● 哈希表的建立比较耗费时间
● 适用处,对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

(16) STL中vector的实现

注意两个点:
1.vector有备用空间,当备用空间不够的时候,会重新开辟原空间两倍的空间进行重写分配。
2.vector支持随机的存取,但是最好是选择从末尾插入,因为从中间插入会导致元素的移动,带来了性能的开销。

(17) vector使用的注意点及其原因,频繁对vector调用push_back()对性能的影响和原因。

vector压入容器的对象都是拷贝操作,而且vector的数据存放都是连续存储的,所以在操作vector操作时,应该尽量避免对尾部操作之后的地方插入删除操作,因为这样会造成元素的移动,造成大量的开销。

频繁对vector调用push_back()会导致性能下降,这是由于系统每次给vector分配固定大小的空间,这个空间可能比用户想分配的空间大一些,但是频繁的使用push_back向容器中插入元素,会导致内存分配空间不够,会再次将整个对象的存储空间重新分配,将旧的元素移动到新的空间中,这样扥开销是非常大的,所以不能频繁的对vector调用push_back()。

(18) C++中的重载和重写的区别:

重载:函数名相同,函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同。函数返回值的类型可以相同,也可以不相同。发生在一个类内部。

重定义:也叫做隐藏,子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) ,指派生类的函数屏蔽了与其同名的基类函数。发生在继承中,查找方式是从子类到父类。

重写:也叫做覆盖,一般发生在子类和父类继承关系之间。子类重新定义父类中有相同名称和参数的虚函数。
http://blog.csdn.net/u010275850/article/details/45583705

(19) C ++内存管理(热门问题)

c++的内存管理延续c语言的内存管理,但是也增加了其他的,例如智能指针,除了常见的堆栈的内存管理之外,c++支持智能指针,智能指针的对象进行赋值拷贝等操作的时候,每个智能指针都有一个关联的计数器,该计数器记录共享该对象的指针个数,当最后一个指针被销毁的时候,计数器为0,会自动调用析构函数来销毁函数。

(20) 介绍面向对象的三大特性,并且举例说明每一个。

面向对象的三大特性:封装、继承、多态。

封装:将很多有相似特性的内容封装在一个类中,例如学生的成绩学号、课程这些可以封装在同一个类中;

继承:某些相似的特性,可以从一个类继承到另一个类,类似生活中的继承,例如有个所有的汽车都有4个轮子,那么我们在父类中定义4个轮子,通过继承获得4个轮子的功能,不用再类里面再去定义这4个轮子的功能。

多态:多态指的相同的功能,不同的状态,多态在面向对象c++里面是通过重载和覆盖来完成的,覆盖在c++里面通过虚函数来完成的。例如鸭子的例子,所有的鸭子都有颜色,我们可以将这个颜色设置成为一个虚函数,通过继承子类对虚函数进行覆盖,不同子类中有各自的颜色,也就是有各自不同的鸭子颜色,这就是多态的典型表现之一。

(21) 多态的实现(和下个问题一起回答)

(22) C++虚函数相关(虚函数表,虚函数指针),虚函数的实现原理(热门,重要)

多态通过覆盖和重载来完成。

虚函数分为两种,纯虚函数和虚函数,纯虚函数适用于抽象基类,不需要定义,类似一种接口,是多态的典型处理方式。

一个类如果定义了虚函数,那么编译器会自动为它加上一个虚函数表,并提供一个指向虚函数表的指针,子类通过继承,可以覆盖父类的虚函数,当用户调用虚函数的时候,会调用指针,去虚函数表中找匹配的虚函数,如果当前对象有覆盖的虚函数,则去执行覆盖的虚函数,否则执行父类的虚函数。

(23) 实现编译器处理虚函数表应该如何处理

虚函数表的主要目的是提供一张表,表上记录子类父类的虚函数地址,通过虚函数表可以达到动态绑定的目的。

编译器首先在编译阶段完成虚函数表的建立,然后当给父类的指针初始化指向的是哪个子类,编译器按照绑定的子类去虚函数表中找对应子类的虚函数,并绑定,这样达到了动态绑定的目的,主要这个绑定是发生在运行的阶段。

(24) 析构函数一般写成虚函数的原因

因为在继承中,我们最后要销毁对象的时候,会调用析构函数,这个时候我们希望析构的是子类的对象,那么我们需要调用子类的析构函数,但是这个时候指针又是父类的指针,所以这个时候我们也要对析构函数写成虚构函数,这样析构函数的虚属性也会被继承,那么无论我们什么时候析构,都能动态绑定到我们需要析构的对象上。

(25) 构造函数为什么一般不定义为虚函数

三个原因:
1.虚函数的作用是什么?是实现部分或默认的功能,而且该功能可以被子类所修改。如果父类的构造函数设置成虚函数,那么子类的构造函数会直接覆盖掉父类的构造函数。而父类的构造函数就失去了一些初始化的功能。这与子类的构造需要先完成父类的构造的流程相违背了。而这个后果会相当严重。

2.虚函数的调用是需要通过“虚函数表”来进行的,而虚函数表也需要在对象实例化之后才能够进行调用。在构造对象的过程中,还没有为“虚函数表”分配内存。所以,这个调用也是违背先实例化后调用的准则。

3.虚函数的调用是由父类指针进行完成的,而对象的构造则是由编译器完成的,由于在创建一个对象的过程中,涉及到资源的创建,类型的确定,而这些是无法在运行过程中确定的,需要在编译的过程中就确定下来。而多态是在运行过程中体现出来的,所以是不能够通过虚函数来创建构造函数的,与实例化的次序不同也有关系。

那么虚够函数为什么可以设计成虚函数呢?由于虚函数是释放对象的时候才执行的,所以一开始也就无法确定析够函数的。而去由于析构的过程中,是先析构子类对象,后析构父类对象。所以,需要通过虚函数来指引子类对象。所以,如果不设置成虚函数的话,析构函数是无法执行子类的析构函数的。

http://blog.csdn.net/helinlin007/article/details/51540182

(26) 构造函数或者析构函数中调用虚函数会怎样?

程序会崩溃

为什么呢?这是由于构造函数或者析构函数中调用虚函数这个时候,子类或许出于一个未初始化的状态,因为c++中父类先构造然后是子类,那么父类中构造调用子类,都没有构造,调用子类的虚函数,显然是错误的。

http://blog.csdn.net/linpengbin/article/details/51560276

(27) 纯虚函数

纯虚函数类似java中的接口,因为在实际的一些策略中,我们并不关心用户创建一个对象,而是希望 有一个通用的概念,或者说是接口,这个就是纯虚函数的目的。

纯虚函数不需要定义,我们不能够为纯虚函数提供函数体,同样的,包含纯虚函数的基类是抽象基类,抽象基类是不能创建对象的,只能通过继承,继承子类中覆盖纯虚函数,执行自己的功能,子类是可以创建对象的。

(28) 静态绑定和动态绑定的介绍

静态绑定:通过用户定义指针指向的类型来进行绑定,在编译的时候已经完成。

动态邦迪:c++中虚函数的功能,通过虚函数表,在运行阶段进行绑定,即运行的时候才知道绑定的函数。

(29) 引用是否能实现动态绑定,为什么引用可以实现

可以实现,因为动态绑定是发生在程序运行阶段的,c++中动态绑定是通过对基类的引用或者指针调用虚函数时发生。

因为引用或者指针的对象是可以在编译的时候不确定的,如果是直接传对象的话,在程序编译的阶段就会完成,对于引用,其实就是地址,在编译的时候可以不绑定对象,在实际运行的时候,在通过虚函数绑定对象即可。

(30) 深拷贝和浅拷贝的区别(举例说明深拷贝的安全性)

深拷贝就是拷贝内容,浅拷贝就是拷贝指针

浅拷贝拷贝指针,也就是说同一个对象,拷贝了两个指针,指向了同一个对象,那么当销毁的时候,可能两个指针销毁,就会导致内存泄漏的问题。

深拷贝不存在这个问题,因为是首先申请和拷贝数据一样大的内存空间,把数据复制过去。这样拷贝多少次,就有多少个不同的内存空间,干扰不到对方

https://www.cnblogs.com/always-chang/p/6107437.html

(31) 对象复用的了解,零拷贝的了解

对象复用指得是设计模式,对象可以采用不同的设计模式达到复用的目的,最常见的就是继承和组合模式了。

零拷贝:零拷贝主要的任务就是避免CPU将数据从一块存储拷贝到另外一块存储,主要就是利用各种零拷贝技术,避免让CPU做大量的数据拷贝任务,减少不必要的拷贝,或者让别的组件来做这一类简单的数据传输任务,让CPU解脱出来专注于别的任务。这样就可以让系统资源的利用更加有效。

零拷贝技术常见linux中,例如用户空间到内核空间的拷贝,这个是没有必要的,我们可以采用零拷贝技术,这个技术就是通过mmap,直接将内核空间的数据通过映射的方法映射到用户空间上,即物理上共用这段数据。

https://www.jianshu.com/p/fad3339e3448

(32) 介绍C++所有的构造函数

默认构造函数、一般构造函数、拷贝构造函数

默认构造函数(无参数):如果创建一个类你没有写任何构造函数,则系统会自动生成默认的构造函数,或者写了一个不带任何形参的构造函数

一般构造函数:一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)

拷贝构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中。参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回。

(33) 什么情况下会调用拷贝构造函数(三种情况)

(1)用类的一个对象去初始化另一个对象时
(2)当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
(3)当函数的返回值是类的对象或引用时

(34) 结构体内存对齐方式和为什么要进行内存对齐?

1.前面的地址必须是后面的地址正数倍,不是就补齐
2.整个Struct的地址必须是最大字节的整数倍

为什么要?

空间换时间,加快cpu访问内存的效率,这是因为许多计算机系统对基本数据类型合法地址做出了一些限制,要求某种类型对象的地址必须是某个值K(通常是2、4或8)的倍数。这种对齐限制简化了形成处理器和存储器系统之间接口的硬件设计

https://www.cnblogs.com/jijiji/p/4854581.html

(35) 内存泄露的定义,如何检测与避免?

内存泄漏指的是开辟的内存没有释放,或者是存在用户操作的错误,导致野指针,无法释放原来分配的内存。

工具监测:在vs里面支持CRT这个库函数,函数里面有内存监测工具,可以调用,在程序中判断内存时否有泄漏。

人为监测:观测所有new开辟内存空间的地方有没有free掉。

避免:在编程习惯上要注意使用尽量使用STL函数,使用vector而不是数组,使用智能指针而不是指针。

(36) 手写实现智能指针类

template <class T> class SmartPointer {
public:
	//普通构造函数, 设定T * ptr的值,并将引用计数设为1
	SmartPointer(T * ptr) {
		ref = ptr;
		ref_count = new unsigned;
		*ref_count = 1;
	}
	
	//指针拷贝构造函数,新建一个指向已有对象的智能指针
	//需要先设定ptr和ref_count
	//设为指向sptr的ptr和ref_count
	//并且,因为新建了一个ptr的引用,所以引用计数加一
	SmartPointer(SmartPointer<T> &sptr) {
		ref = sptr.ref;
		ref_count = sptr.ref_count;
		++(*ref_count);
	}

	//rewrite "="
	SmartPointer<T> & operator = (SmartPointer<T> &sptr) {
        //同一个指针,直接返回
        if (this == &sptr)
            return *this;

        //如果计数值大于1,则旧指针计数值-1
		if(*ref_count > 0)
            remove();

        //把旧指针值给新指针
		ref = sptr.ref;
		ref_count = sptr.ref_count;

        //指针计数+1
		++(*ref_count);
		return *this;
	}
	~SmartPointer() {
		remove();
	}

	T getValue() {
		return *ref;
	}

	T getCount() {
		return static_cast<T>(*ref_count);
	}
protected:
    //删除指针
	void remove() {
		--(*ref_count);

		//如果计数值等于0,则销毁指针,并执行析构函数
		if (*ref_count == 0) {
			delete ref;
			delete ref_count;
			ref = NULL;
			ref_count = NULL;
		}
	}
private:
	unsigned * ref_count;    //应用计数值
	T * ref;                 //普通指针
};

(37) 调试程序的方法

这个方式很多,裸机程序,主动调试,gdb调试,IDE断点调试等。

(38) 遇到coredump要怎么调试

内存泄漏的方法很多,可以用gdb打开core文件,确定出错的堆栈地点,从而判断程序出错的位置。

(39) 内存检查工具的了解

在vs里面支持CRT这个库函数

(40) 模板的用法与适用场景

模板是C11里面添加的,使用与在不知道类型的情况下,编写一个泛型的程序,模板通过用一个指定的关键字来代替类型,进行泛型编程。

应用场景:应用场景很多,例如我们要编程一些和类型无关的代码时,STL里面的很多容器都是用到了模板,容器的功能都可以使用,但并没有确定容器里面一定要用指定的类型,可以是任何的类型。

(41) 成员初始化列表的概念,为什么用成员初始化列表会快一些(性能优势)?

成员初始化的概念,就是说类的成员使用在定义的时候就使用构造函数初始值列表初始化

使用成员初始化要快些,这里说的快些是比较的是赋值,如果指定定义变量,没有列表初始化,那么这样变量旧会执行默认的初始化,然后在赋值,这样就多了一次赋值操作,带来的开销取决于数据成员的类型。

除了效率之外,有一些成员必须列表初始化,例如const或者引用

(42) 用过C11吗,知道C11新特性吗?(有面试官建议熟悉C11)

用过,C11有许多新的特性

例如:auto、decltype,nullptr,for(auto i:m),lambda表达式,智能指针等。

(43) C++的调用惯例(简单一点C++函数调用的压栈过程)

函数调用大家都不陌生,调用者向被调用者传递一些参数,然后执行被调用者的代码,最后被调用者向调用者返回结果。

对于程序,编译器会对其分配一段内存,在逻辑上可以分为代码段,数据段,堆,栈

代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写
数据段:保存初始化的全局变量和静态变量,可读可写不可执行
BSS:未初始化的全局变量和静态变量
堆(Heap):动态分配内存,向地址增大的方向增长,可读可写可执行
栈(Stack):存放局部变量,函数参数,当前状态,函数调用信息等,向地址减小的方向增长,非常非常重要,可读可写可执行

程序开始,从main开始,首先将参数压入栈,然后压入函数返回地址,进行函数调用,通过跳转指定进入函数,将函数内部的变量去堆栈上开辟空间,执行函数功能,执行完成,取回函数返回地址,进行下一个函数。

(44) C++的四种强制转换

四种强制转换是static_cast、dynamic_cast、const_cast、reinterpret_cast。

static_cast:静态强制转换,类似传统c语言里面括号的强制转换
dynamic_cast:动态强制转换,主要应用于多态,父子类的类型转换,dynamic_cast和static_cast不同的是,它会检查类型转换是否正确,不能转换,则会返回null,所以不算是强制转换。
const_cast:取出const属性,比较简单,可以把const类型转换为非conse指针类型。
reinterpret_cast:一种非常随意的二进制转换,简单理解对一个二进制序列的重新解释。

http://blog.csdn.net/swartz2015/article/details/69651482
https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/23/2652696.html

猜你喜欢

转载自blog.csdn.net/u012414189/article/details/83856874
今日推荐