c++类常见
类
构造函数
什么情况下会合成构造函数?
- 如果一个类没有任何构造函数,但他含有一个成员对象,该成员对象含有默认构造函数,那么编译器就为该类合成一个默认构造函数,因为不合成一个默认构造函数那么该成员对象的构造函数不能调用;
- ,没有任何构造函数的类派生自一个带有默认构造函数的基类,那么需要为该派生类合成一个构造函数,只有这样基类的构造函数才能被调用;
- 带有虚函数的类,虚函数的引入需要进入虚表,指向虚表的指针,该指针是在构造函数中初始化的,所以没有构造函数的话该指针无法被初始化;
- 带有一个虚基类的类
并不是任何没有构造函数的类都会合成一个构造函数
编译器合成出来的构造函数并不会显示设定类内的每一个成员变量
拷贝构造函数
class A { A(const A & a);};
对象不存在,使用别的已存在的对象来初始化时会用到拷贝构造函数
对象存在,则是用赋值运算符,因此赋值运算符需要在初始化对象前检查源对象和新建对象是否相同
移动构造函数
用a初始化b后,就把a析构,采用的是浅拷贝
为什么要用引用?
拷贝构造函数会在以下情况中被调用:1. 函数参数中作为值传递 2. 不作优化时,将类对象作为函数返回值 3. A = B,调用拷贝构造函数
当不使用引用而使用值传递时,拷贝构造函数本身又会调用自身,不断嵌套下去直到栈溢出
引用可以改成指针吗?
不可以,改成指针后会变成普通的有参构造函数
什么时候会生成默认拷贝构造函数?
当符合位拷贝语义时,无需生成默认拷贝构造函数,而是会直接调用构造函数
当不符合位拷贝语义时,才需要生成默认拷贝构造函数:1. 类有内部类对象,包含拷贝构造函数 2. 类继承于基类,基类有拷贝构造函数 3. 类中含有虚函数,需要有默认的拷贝构造函数来完成对虚函数指针的指向,否则Base base = thrive
虚指针发生切割行为,4. 类存在虚继承,也会发生虚指针漂移,需要生成默认的拷贝构造函数
多态
多态是指指向派生类的基类指针在运行时,可以根据派生类对象类型来对不同虚函数进行调用。底层原理是当派生类对基类的虚函数进行重写时,派生类的虚表指针指向的是自身的虚表,而不是基类的虚表。(编译器自动为每个含有虚函数的类生成一份虚表)
析构函数为什么要写成虚函数
由于类的多态性,可以有指向派生类的基类指针,这时如果删除基类指针,会调用指向的派生类对象的析构函数,派生类析构函数自动调用基类的析构函数,这样整个派生类对象完全被释放。如果析构函数不是虚函数,就会静态绑定到基类,删除基类指针时只会调用基类的析构函数,而导致内存泄露
构造函数能声明为虚函数或纯虚函数吗?析构函数呢
构造函数不能声明为虚函数或纯虚函数,因为如果构造函数是虚函数,虚函数的调用通过虚指针和虚表,但虚表需要在类对象初始化后才有,无法找到调用所需的虚表
一般情况下基类析构函数是虚函数,也可以是纯虚函数,含有纯虚函数的类是抽象类,不能被实例化
虚表放在内存的什么区,虚指针初始化时间
虚函数表在类中共享,全局只有一个,在编译时构造完成。
派生类在不重写基类虚函数时,虚表地址与基类不同,虚表中虚函数地址与基类中虚函数地址相同
派生类在重写基类虚函数时,虚表地址与基类不同,虚表中虚函数地址也与基类中虚函数地址不同
每个类对象的前四个字节保存虚指针,指向虚函数表。
由于虚表的元素是虚函数的地址,不是程序代码,也就不会存储在代码段,并且类中的虚函数个数在编译期确定,不必动态分配,也就不会在堆区。所以虚表储存只读数据段,也就是常量区当中,虚函数则储存在代码段。C++内存模型有堆区、栈区、常量区、代码区和数据区(BSS未初始化段、已初始化段)
构造函数、析构函数、虚函数可否声明为内联函数
构造函数和析构函数声明为内敛函数没有意义。
虚函数只有当指向类本身的指针调用时,才会内联展开;当指向派生类的指针调用时(多态),并不会内联展开
构造函数、析构函数可否抛出异常
构造函数可以抛出异常,当异常抛出,未完成的对象不会被创建,自动调用已构造对象的析构函数释放已分配资源。但因为析构函数不能被调用,可能会造成内存泄露
析构函数不建议抛出异常,如果析构函数在另一个异常正在传播时,会导致程序异常终止
因此,尽量避免手动管理资源, 使用RAII类进行封装。为了避免在析构函数中抛出异常,可以捕获并处理异常:在析构函数内部捕获所有可能的异常,避免将异常抛出;**使用 noexcept
**:将析构函数声明为 noexcept
(默认情况下,析构函数是 noexcept(true)
),确保在析构过程中不会抛出异常
虚拟继承作用
虚继承是一种解决菱形继承问题的机制,确保在多重继承下,基类只被继承一次。最低层的派生类负责虚基类的构造