今天面试问了道题,之前没有想过,说构造函数或者析构函数中有虚函数,那实际执行的是哪个函数,面试完进行了测试,如下代码所示:
#include <iostream>
class A
{
public:
A() {
printf("*** A() begin ***\n");
f();
g();
printf("*** A() end ***\n\n");
}
void g() { printf("A g()\n"); }
virtual void f() { printf("A f()\n"); }
virtual ~A() // 不加 virtual 会只执行父类的析构
{
printf("*** ~A() begin ***\n");
f();
g();
printf("*** ~A() end ***\n\n");
}
};
class B : public A
{
public:
B() {
printf("*** B() begin ***\n");
f();
g();
printf("*** B() end ***\n\n");
}
void g() { printf("B g()\n"); }
void f() override { printf("B f()\n"); }
~B() {
printf("*** ~B() begin ***\n");
f();
g();
printf("*** ~B() end ***\n\n");
}
};
int main()
{
A* p = new B;
printf("p->f() -> ");
p->f();
printf("p->g() -> ");
p->g();
printf("\n");
delete p;
return 0;
}
输出如下:
*** A() begin ***
A f()
A g()
*** A() end ***
*** B() begin ***
B f()
B g()
*** B() end ***
p->f() -> B f()
p->g() -> A g()
// A 中析构函数如果不是虚函数,则没有这一段输出
*** ~B() begin ***
B f()
B g()
*** ~B() end ***
// A 中析构函数如果不是虚函数,则没有这一段输出
*** ~A() begin ***
A f()
A g()
*** ~A() end ***
从上边的代码和结果可以看出:
1、构造函数执行顺序是:先父类,后子类;析构函数的执行顺序是:先子类,后父类。
2、父类的析构函数必须是虚函数,否则 delete 时候不会执行子类析构函数,会内存泄漏。
3、同名,同参虚函数能正常达到多态效果,不是虚函数就会出现 “隐藏”,父类指针只能调用父类函数,ReSharper提示如下。
4、TX的面试题:就是类的构造函数和析构函数中,虚函数表现不出多态!
在VS中,ReSharper 会提示构造函数和析构函数中的虚函数会被 静态处理,也就是用这个类自己的,如下图。