现在觉得算法题目不是最难的了,起码还可以用最朴素的想法写一个,考特性才是真的惨。
构造函数不应该是虚函数
#include <iostream>
#include <cmath>
using namespace std;
class A
{
public:
virtual A(){cout << "Construct A" << endl;}
~A(){cout << "Destruct A" << endl;}
};
class B: public A
{
public:
virtual B(){cout << "Construct B" << endl;}
~B(){cout << "Destruct B" << endl;}
};
int main(int argc, char const *argv[])
{
B b;
return 0;
}
这样的代码在clang下会报错,因为把构造函数定义为了虚函数。这样做是不允许的,因为在类被构造的时候,是会先调用父类的构造函数,最后才会调用子类的构造函数,如果申明为虚函数,那么会出现歧义。
析构函数应该定义为虚函数
#include <iostream>
#include <cmath>
using namespace std;
class A
{
public:
A(){cout << "Construct A" << endl;}
~A(){cout << "Destruct A" << endl;}
};
class C
{
public:
C(){cout << "Construct C" << endl;}
~C(){cout << "Destruct C" << endl;}
};
class B: public A
{
public:
B(){cout << "Construct B" << endl;}
~B(){cout << "Destruct B" << endl;}
C c;
};
int main(int argc, char const *argv[])
{
A *a = new B();
delete a; // ~A() or ~B()?
return 0;
}
上面这段代码的输出为
Construct A Construct C Construct B Destruct A
构造顺序在之前的文章中提到过了,那么析构的时候为什么只会析构A呢?这是因为现在是用一个指向B类型的变量的A类型的指针来调用析构函数,所以,在析构函数不是虚函数的时候,析构函数是编译时静态绑定的,所以A指针调用的析构函数一定是A定义的析构函数。
而将A和B的析构函数定义为虚函数之后,输出变成了这样:
Construct A Construct C Construct B Destruct B Destruct C Destruct A
这就是虚函数动态绑定析构函数起了作用,这里涉及到虚函数的一个特性,如果使用指向对象的引用或者是指针来调用虚方法,程序将使用对象类型定义的方法,而不使用引用或指针类型定义的方法,这样基类的指针或者引用就可以指向派生类而调用派生类的虚函数。这也就是为什么析构函数应该声明为虚函数的原因所在。