0%

C++ 构造函数与析构函数

C++ 中构造函数与析构函数一些注意事项

构造函数中不能调用虚函数

在 C++ 中,实例化一个子类对象的时候,会先构造父类部分, 父类部分构造完成后再构造子类部分

因此,在父类构造函数调用的时候,子类部分数据还未构造完成, 此时调用子类方法本身就是未定义行为 , 所以 C++ 中构造函数中调用虚函数并不会表现出多态特征

为什么析构函数需要定义为虚函数

一个常见的面试问题:为什么 C++ 中的析构函数需要定义为虚函数?

通常 C++ 析构一个对象时,会依次调用对象的析构函数和它父类的析构函数, 但是在多态发生时,可能会漏调用对象本身的析构函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
public:
~A() { printf("~A()\n"); }
}
class B : public A
{
public:
~B() { printf("~B()\n"); }
}
int main()
{
A* ptr = new B();
delete ptr;
return 0;
}

以上代码是析构函数不使用虚函数的错误写法, 我们在释放父类指针所指向的对象时, 希望析构函数调用是先 ~B()~A(), 但以上代码只会调用 ~A() 而对象本身属于类 B 的部分不会调用析构函数释放, 发生内存泄漏

如果将析构函数写成虚函数, 那么释放父类指针指向的对象时会根据多态原理调用正确的子类析构函数

析构函数中不能调用虚函数

同理,我们将析构函数写成虚函数, 为了析构函数能从子类到父类一层层释放资源, 所以当父类的析构函数被调用时,子类的数据已经被析构, 此时调用子类的方法也是未定义行为 , 所以 C++ 析构函数中调用虚函数也不会表现出多态

至于是不是虚表指针没有初始化, 还是编译器在构造函数和虚构函数中更改了虚表指针的值,这都不重要, 反正记住编译器的实现是不表现出多态特征

总结:

  • C++ 父类构造函数调用时,子类并未构造完成
  • C++ 父类析构函数调用时,子类已经析构
  • C++ 编译器实现:在构造函数和析构函数中调用虚函数并没有多态特性

不要在构造函数和析构函数中调用虚函数