现在觉得算法题目不是最难的了,起码还可以用最朴素的想法写一个,考特性才是真的惨。

构造函数不应该是虚函数

#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

这就是虚函数动态绑定析构函数起了作用,这里涉及到虚函数的一个特性,如果使用指向对象的引用或者是指针来调用虚方法,程序将使用对象类型定义的方法,而不使用引用或指针类型定义的方法,这样基类的指针或者引用就可以指向派生类而调用派生类的虚函数。这也就是为什么析构函数应该声明为虚函数的原因所在。

评论