构造函数析构函数中能不能使用虚函数
面试时有一个经典问题是 “在构造函数和析构函数中能否调用虚函数”,准备面试时,我还没有系统学习 C++,故而硬背的答案:不能。记得腾讯技术终面的时候面试官也问了我这个问题,我较为熟练地说出了自己的答案,如在构造函数中能否使用的回答大致为:
不能。我想从两个方面来回答这个问题。首先一个对象的虚函数表是在其构造过程中生成,构造过程中还没有完全生成好,在这个角度看在构造函数中调用虚函数是不合理的。第二,构造过程中,编译器按照从基类到派生类的顺序依次构造,在一些阶段处于未完成状态,这时编译器会将我们的对象看做是其当前状态的类,而非目的类的当前类阶段,故不可以调用虚函数。
现在几天在看 《C++ Primer》,打算系统地学习下 C++。在 P556 页中,刚好讲到了这个问题,读起来总有些不懂的地方,于是特意上网搜索了下,搜到了份我感觉不错的代码,在这解析下。
| 1 | 
 | 
自己运行了下,其结果为
| 1 | 0 | 
解析下程序,我们首先看到在 A、B 两类的构造函数中都调用了非虚函数 test(), 而 test() 函数功能则是调用虚函数 func(), 在 A、B 两类中都定义了自己的 func 函数, A 的 func 直接输出 val, 而 B 的 func 则先自增 val , 而后输出。
在  main 函数中,A* p = new B 构造了一个类 B 的对象,而将其赋给基类 A 的指针。在构造 B 的对象时,我们依次调用了 A 的构造函数和 B 的构造函数。A 的构造函数向 val 赋值为 0,并输出它,这是结果中输出 0 的缘由。在 B 的构造函数中,我们调用 this->test() , test() 函数又调用了 this->func(),故调用了类 B 版本的 func() 函数,这是结果中 1 的来源。
main 函数中 p->test() 语句,由于 test() 为非虚函数,故静态绑定到了类 A 的 test() 函数,test() 函数调用 this->func() 即 p->func(),为动态绑定,故绑定到类 B 的 func() 函数,故输出 2。
如此看来,在构造函数、析构函数中调用虚函数是有规律可循的,但较易出错。假若我们要使用时,一定要理解其中机制,合理谨慎设计。如果让我再回答这个问题,我较倾向回答为谨慎使用、尽量不使用。