十八万字吐血整理的C/C++、嵌入式常见面试题宝典!!!!
相关知识点都能零星在网上找到,这个文章系列将目前遇到的所有常见面试问题进行一个汇总。
文中很多资料避免不了从网上或是其他复习资料里收集整理,十分感谢前辈的辛勤付出,如果存在侵权请一定联系我进行删除。
系列文章PDF下载地址:《最全C_C++及嵌入式软开面试题宝典.pdf》
21、 为什么拷贝构造函数必需时引用传递,不能是值传递?
为了防止递归调用。
当⼀个对象需要以值⽅式进⾏传递时,编译器会⽣成代码调⽤它的拷⻉构造函数⽣成⼀个副本,如果类A的拷⻉构造函数的参数不是引⽤传递,⽽是采⽤值传递,那么就⼜需要为了创建传递给拷⻉构造函数的参数的临时对象,⽽⼜⼀次调⽤类 A 的拷⻉构造函数,这就是⼀个⽆限递归。
1) 拷贝构造函数的作用就是用来复制对象的,再使用这个对象的实例来初始化这个对象的一个新的实例。
2) 参数传递过程到底发生了什么?将地址传递和值传递统一起来,归根结底还是传递的是"值"(地址也是值,只不过通过它可以找到另一个值)!
i)值传递:
对于内置数据类型的传递时,直接赋值拷贝给形参(注意形参是函数内局部变量);
对于类类型的传递时,需要首先调用该类的拷贝构造函数来初始化形参(局部对象);如void foo(class_type obj_local){}, 如果调用foo(obj); 首先class_type obj_local(obj) ,这样就定义了局部变量obj_local供函数内部使用
ii)引用传递:
无论对内置类型还是类类型,传递引用或指针最终都是传递的地址值!而地址总是指针类型(属于简单类型), 显然参数传递时,按简单类型的赋值拷贝,而不会有拷贝构造函数的调用(对于类类型)。
上述1) 2)回答了为什么拷贝构造函数使用值传递会产生无限递归调用,内存溢出。
拷贝构造函数用来初始化一个非引用类类型对象,如果用传值的方式进行传参数,那么构造实参需要调用拷贝构造函数,而拷贝构造函数需要传递实参,所以会一直递归。
22、何时需要合成构造函数
1.如果一个类没有任何构造函数,但他含有一个成员对象,该成员对象含有默认构造函数,那么编译器就为该类合成一个默认构造函数,因为不合成一个默认构造函数那么该成员对象的构造函数不能调用;
2.没有任何构造函数的类派生自一个带有默认构造函数的基类,那么需要为该派生类合成一个构造函数,只有这样基类的构造函数才能被调用;
3.带有虚函数的类,虚函数的引入需要进入虚表,指向虚表的指针,该指针是在构造函数中初始化的,所以没有构造函数的话该指针无法被初始化;
4.带有一个虚基类的类
5.并不是任何没有构造函数的类都会合成一个构造函数
6.编译器合成出来的构造函数并不会显示设定类内的每一个成员变量
23、何时需要合成复制构造函数
有三种情况会以一个对象的内容作为另一个对象的初值:
1.对一个对象做显示的初始化操作,X xx = x;
2.当对象被当做参数交给某个函数时;
3.当函数传回一个类对象时;
24、 C++类的虚函数表和虚函数在内存中的位置
关系:虚函数表指针(保存在堆或栈)->虚函数表(常量区 .rodata)->虚函数(代码段 .text)
虚函数表指针是虚函数表所在位置的地址。虚函数表指针属于对象实例。因而通过new 出来的对象的虚函数表指针位于堆,声名对象的虚函数表指针位于栈。
总结:
1、虚函数表指针位置取决于对象在哪。如果是new的对象,则存在堆上,如果是直接声明,则存在栈上。
2、虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区;
3、虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区。
25、 同一个类,实例化多次,是否共享虚函数表?
所有实例是共用一个虚函数表。
1.为什么要共用同一个虚函数表?可能是为了节省内存吧,首先同一个类的对象虚函数都是一样的,没必要重新伴随构造生成一份一模一样的表,所以拷贝虚函数表的表地址就行。
2.C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——假设有多层继承或是多重继承的情况下)。
3.这意味着我们通过对象实例的地址得到这张虚函数表。然后就能够遍历当中函数指针,并调用对应的函数。