构造函数、析构函数、虚析构函数、纯虚析构函数要点总结

1、派生类的构造函数、析构函数默认会调用基类的构造函数、析构函数。

顺序:基类构造、派生类构造;派生类析构,基类析构。

举例:

/******************************************************************************************************
* File:Constructor&DestructorTest
* Introduction:测试构造函数、析构函数、虚构造函数与纯虚构造函数的一些特性。
* Author:CoderCong
* Date:20141013
* LastModifiedDate:20160113
*******************************************************************************************************/
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base
{
public:
	Base()
	{
		cout << "Base:Constructing" << endl;
	}
	~Base()
	{
		cout << "Base: Destructing!" << endl;
	}
};
class Derived:public Base
{
public:
	Derived()
	{
		cout << "Derived:Constructing" << endl;
	}
	~Derived()
	{
		cout << "Derived: Destructing!" << endl;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	Derived *pDerived=new Derived;
	delete pDerived;
	return 0;
}

运行结果:
Base:Constructing 
Derived:Constructing 
Derived: Destructing! 
Base: Destructing!

2、如果你的类用作派生类的基类,最好定义其析构函数为虚函数。

好处:定义其析构函数为虚函数,可以利用动态绑定,把一个子类对象绑定到一个基类指针上时,可以自动调用基类的析构函数。从而默认地调用基类的析构函数。

举例: 

同样是上边的函数,我们把_tmain函数改为这样:

	Base *pBase = new Derived;
	delete pBase;
	

同志们注意运行结果: 

Base:Constructing 
Derived:Constructing 
Base: Destructing!

天哪!居然没有调用派生类的析构函数! 
注意,你delete的指针是Base类的指针。而Base的析构函数不是虚函数。当然不用调用派生类的析构函数! 
天真的你也许会说,这也没什么大不了的吧?

没什么大不了的?再也没有比这更大的事情了。这可能会造成一个严峻的问题:内存泄露!我们真正想删除的是一个派生类对象,而实际上只是删除了派生类对象中基类的那一部分。如果派生部分有一些需要释放的资源,如网络、内存等,那么这些资源将无法得到释放。

作为一个严谨的C++程序员,我们当然不允许这样的事情发生。我们把Base类的析构函数改为虚函数试试看:

virtual ~Base()
	{
		cout << "Base: Destructing!" << endl;
	}
运行结果:
Base:Constructing 
Derived:Constructing 
Derived: Destructing! 
Base: Destructing!

我们惊喜得发现,一切都正常了。

再来理一遍:

我们new一个Derived类对象,调用了Derived类的构造函数,而此构造函数又默认地调用了基类Base的析构函数;

然后,我们把这个Derived对象绑定到Base类指针pBase,这时候,这个pBase的静态类型是Base*,动态类型却是Derived*。我们删除pBase,系统跟据动态绑定,最终调用的是~Derived,而不是~Base。调用~Derived之后,系统又默认地调用了基类析构函数~Base。

就是这样。

3、为什么构造函数不能是虚函数?

当我们想把构造函数也设为虚函数的时候,会发现,系统是不允许的?为什么呢?
查过资料之后,发现这么一段解释:
    A virtual call is a mechanism to get work done given partial information. In particular, "virtual" allows us to call a function knowing only an interfaces and not the exact type of the object. To create an object you need complete information. Inparticular, you need to know the exact type of what you want tocreate. Consequently, a "call to a constructor" cannot be virtual.

翻译一下:虚函数只需要”部分“信息就可以自动调用,特别地,这种机制允许我们在只知道接口,不知道具体对象的类型的情况下调用函数。而实例化一个对象需要完整的信息,特别地,必须知道实例化对象的确切类型。总之,构造函数的调用不能是虚的。
这下子我们就知道为什么了。具体虚函数的实现机制,我们随后的博文中会介绍。今天我们只讲构造函数和析构函数的事。

4、基类有纯虚构造函数时为什么会连接错误?

上代码:
virtual ~Base() =0;

这时,我们在实例化一个派生类对象时,发生连接错误,如下图所示:
                               
这是为什么呢?我们想一下在调用Derived类的析构函数的时候发生了什么?首先调用~Derived,因为我们并没有动这个函数,所以这一步是没有问题的。之后,又调用了~Base,可是系统找不到Base的函数体?这就是问题所在了吧?不确定,我们试试看。
把一个空的~Base的函数体加上看:
Base::~Base(){}
程序正常运行了。说明我们的猜测是正确的。
就是这样。

打破罐子问到底,多问为什么?为什么要用纯虚析构函数,有什么用?
我在想,纯虚函数是为了解决什么问题?纯虚函数是为了实现抽象类,也就是说,某个基类,我们不想用户将这个类实例化,最简便的方法,就是将这个基类中的某个函数定义为纯虚函数。
所以,当这个基类中只有构造和析构怎么办?只能拿析构函数来开涮了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章