构造函数析构函数中能不能使用虚函数
面试时有一个经典问题是 “在构造函数和析构函数中能否调用虚函数”,准备面试时,我还没有系统学习 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。
如此看来,在构造函数、析构函数中调用虚函数是有规律可循的,但较易出错。假若我们要使用时,一定要理解其中机制,合理谨慎设计。如果让我再回答这个问题,我较倾向回答为谨慎使用、尽量不使用。