简单总结就是:构造函数不可以是虚函数,而析构函数可以且常常是虚函数。
构造函数不能是虚函数
1.从vptr角度解释
虚函数的调用是通过虚函数表来查找的,而虚函数表由类的实例化对象的vptr指针(vptr可以参考C++的虚函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是虚函数,那么调用构造函数就需要去找vptr,但此时vptr还没有初始化!
2.从多态角度解释
虚函数主要是实现多态,在运行时才可以明确调用对象,根据传入的对象类型来调用函数,例如通过父类的指针或者引用来调用它的时候可以变成调用子类的那个成员函数。而构造函数是在创建对象时自己主动调用的,不可能通过父类的指针或者引用去调用,因此使用虚函数也没有实际意义。并且构造函数的作用是提供初始化,在对象生命期仅仅运行一次,不是对象的动态行为,没有必要成为虚函数。
析构函数可以且常常是虚函数
此时 vtable 已经初始化了,完全可以把析构函数放在虚函数表里面来调用。
C++类有继承时,基类的析构函数必须为虚函数。如果不是虚函数,则使用时可能存在内存泄漏的问题。
假设我们有这样一种继承关系:
如果我们以这种方式创建对象:
SubClass* pObj = new SubClass();
delete pObj;
不管析构函数是否是虚函数(即是否加virtual关键词),delete时基类和子类都会被释放;
如果我们要实现多态,令基类指针指向子类,即以这种方式创建对象:
BaseClass* pObj = new SubClass();
delete pObj;
若析构函数是虚函数(即加上virtual关键词),delete时基类和子类都会被释放;
若析构函数不是虚函数(即不加virtual关键词),delete时只释放基类,不释放子类,会造成内存泄漏问题。
构造函数或者析构函数中调用虚函数会怎样
简要结论:
- 从语法上讲,调用完全没有问题。
- 但是从效果上看,往往不能达到需要的目的。
- 如果在基类中声明了纯虚函数,并且在基类的析构函数中调用之,编译器会发生错误。
Effective 的解释是:
- 由于类的构造次序是由基类到派生类,所以在构造函数中调用虚函数,派生类还没有完全构造,虚函数是不会呈现出多态的。
- 类的析构是从派生类到基类,当调用继承层次中某一层次的类的析构函数时意味着其派生类部分已经析构掉&#x