本文最后更新于:2022年11月22日 下午
其实这来自于 《Effective C++》 Rule 09:
Never call virtual functions during construction or destruction
这一点主要是因为构造函数和析构函数比较特殊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <bits/stdc++.h> using namespace std;class Base {public : Base (){ cout << "Call Base::constructor" << endl; } virtual ~Base (){ cout << "Call Base::destructor" << endl; } };class Derived : public Base{public : Derived (){ cout << "Call Derived::constructor" << endl; } virtual ~Derived (){ cout << "Call Derived::destructor" << endl; } };int main (void ) { Base* d = new Derived; delete d; }
结果如下:
1 2 3 4 5 6 7 D:\Desktop\Study\course\cpp_wkspc\Leetcode\cmake-build-debug\Leetcode.exe Call Base::constructor Call Derived::constructor Call Derived::destructor Call Base::destructor Process finished with exit code 0
子类Derived
对象构造时:
先调用基类Base
构造函数
再调用Derived
构造函数
而Derived
对象析构时:
先调用子类Derived
析构函数
再调用基类Base
析构函数
所以如果在构造函数中调用虚函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Base {public : Base (){ TestConstruct (); cout << "Call Base::constructor" << endl; } virtual void TestConstruct () { cout << "Call Base::TestConstruct" << endl; } virtual void TestDestruct () { cout << "Call Base::TestDestruct" << endl; }; virtual ~Base (){ TestDestruct (); cout << "Call Base::destructor" << endl; } };class Derived : public Base{public : Derived (){ TestConstruct (); cout << "Call Derived::constructor" << endl; } virtual void TestConstruct () { cout << "Call Derived::TestConstruct" << endl; } virtual void TestDestruct () { cout << "Call Derived::TestDestruct" << endl; }; virtual ~Derived (){ TestDestruct (); cout << "Call Derived::destructor" << endl; } };int main (void ) { Base* d1 = new Base (); delete d1; cout << "-----------------------------------" <<endl; Base* d2 = new Derived (); delete d2; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 D:\Desktop\Study\course\cpp_wkspc\Leetcode\cmake-build-debug\Leetcode.exe Call Base::TestConstruct Call Base::constructor Call Base::TestDestruct Call Base::destructor ----------------------------------- Call Base::TestConstruct Call Base::constructor Call Derived::TestConstruct Call Derived::constructor Call Derived::TestDestruct Call Derived::destructor Call Base::TestDestruct Call Base::destructor Process finished with exit code 0
所以如果我们想要在构造函数和析构函数中为子类和父类调用不同版本的虚函数,可能会失望:因为子类实际上会一共执行两个版本的虚函数
这样的行为背后的逻辑是Derived
对象的Base class
构造期间,对象的类型是Base
而不是Derived
,所以 virtual function 会被编译器解析至Base
.这是非常合理的,因为Derived
中的虚函数绝大多数都会使用到属于Derived
部分的成员变量,而那些成员变量在此刻尚未初始化,使用未初始化的成员对象存在风险。
而对于析构函数,进入Base
析构函数后,对象的类型也被视作Base
而非Derived
.由于Derived
的析构函数会先于Base
析构函数执行,当进入Base
析构函数时,Derived
部分的成员变量应当已经被析构而呈现未定义值,所以也不应该使用它们。