常见问题
-
构造函数可以是虚函数吗?
答:构造函数不可以是虚函数。
原因:
构造对象时必须知道对象的实际类型,但是虚函数调用在运行时才能确定对象的实际类型,这会导致编译器无法确定对象的具体类型。
虚函数的执行依赖于虚函数表(vtable),虚函数表在构造函数中进行初始化工作,即初始化vptr(指向虚函数表的指针)。在构造对象期间,虚函数表还没有被初始化,因此无法通过虚函数表来调用构造函数。 -
析构函数可以是虚函数吗?
答:作为父类时,析构函数必须是虚函数。
当通过基类指针删除派生类对象时,基类的析构函数是虚函数时才能正确调用派生类的析构函数,从而避免内存泄漏等问题。 -
构造函数和析构函数也不能调用虚函数,前者是因为虚表指针还没有被初始化,后者是因为虚表指针可能已经被析构了。
-
虚函数表是在编译期间生成的,虚函数表属于类,类的所有对象共享这个类的虚函数表。虚函数表指针在运行时创建,每个实例对象都有一个虚函数表指针。
代码测试
首先定义两个类:
基类:
class Person
{
public:Person(){qDebug()<<"Person 构造函数";}~Person(){qDebug()<<"Person析构函数";}
};派生类:
class Student: public Person
{
public:Student(){qDebug()<<"Student 构造函数";}~Student(){qDebug()<<"Student 析构函数";}
};
输出测试:
Person *pPerson = new Person;delete pPerson;pPerson = nullptr;qDebug()<<endl;Student *pStudent = new Student;delete pStudent;pStudent = nullptr;qDebug()<<endl;Person *p = new Student();delete p;p = nullptr;
输出结果:
删除pPerson时:
Person 构造函数
Person析构函数删除pStudent时:
Person 构造函数
Student 构造函数
Student 析构函数
Person析构函数删除p时:
1. 基类的析构函数不是虚函数时:
Person 构造函数
Student 构造函数
Person析构函数2. 基类的析构函数是虚函数时:
Person 构造函数
Student 构造函数
Student 析构函数
Person析构函数
结果分析
- 创建一个类对象指针时,调用构造函数;删除对象指针时,自动调用析构函数。
- 创建派生类对象指针时,首先调用基类的构造函数,然后调用派生类的构造函数;删除该对象指针时先调用派生类的析构函数,然后调用基类的析构函数。
- 基类指针指向派生类对象时,创建时先调用基类的构造函数,然后调用派生类的构造函数;如果基类的析构函数不是虚函数,那么删除该指针时,只调用基类的析构函数,不调用派生类的析构函数。
- 基类指针指向派生类对象时,创建时先调用基类的构造函数,然后调用派生类的构造函数;如果基类的析构函数是虚函数,那么删除该指针时,先调用派生类的析构函数,再调用基类的析构函数。