条款7:为多态基类声明virtual析构函数

条款7:为多态基类声明virtual析构函数
      C++中明确规定:为derived class对象经由base class指针删除时,而该base class指针带有一个non-virtual函数时,其结果未定义--实际执行时通常发生的是derived对象没有被销毁。

1、时钟例子

class TimeKeeper
{
public:
	TimeKeeper()
	{
		cout<<"TimeKeeper construction\n";
	}

	~TimeKeeper()
	{
		cout<<"TimeKeeper destruction\n";
	}
};

//本例只是说明虚析构函数作用,那些自定义拷贝构造函数、拷贝赋值函数不考虑
class AtomicClock : public TimeKeeper
{
public:
	AtomicClock(const char* time) : TimeKeeper()
	{
		cout<<"AtomicClock construction\n";

		this->m_time = new char[strlen(time) + 1];
		strcpy(this->m_time, time);
	}
	~AtomicClock()
	{
		cout<<"AtomicClock destruction\n";

		if (this->m_time != NULL)
			delete m_time;
		m_time = NULL;
	}
private:
	char *m_time;
};
调用
	TimeKeeper *pTime = new AtomicClock("2014:5:24");
	delete pTime;
	pTime = NULL;
结果如下:
TimeKeeper construction
AtomicClock construction
TimeKeeper destruction

    新创建一个AtomicClock对象,调用基类构造函数,再调用本类构造函数,然后将地址赋给基类指针,释放基类指针,只是调用基类析构函数,AtomicClock对象没有被析构,里面的动态成员没有被释放内存,导致内存泄露。
    消除这种情况,只需要将基类析构函数声明为虚析构函数
	virtual ~TimeKeeper()
	{
		cout<<"TimeKeeper destruction\n";
	}
同样调用,结果如下
TimeKeeper construction
AtomicClock construction
AtomicClock destruction
TimeKeeper destruction

2、虚函数指针

       一个class被当做base class,那些一般都是可以对derived class动态调用,实现对基类客制化,即同一个函数接口可以由derived class实现不同的行为,这通过虚函数接口实现,class中存在虚函数,那么几乎需要存在一个虚析构函数。
       如果一个类不含有虚函数,而且不是当做基类,就为该class声明虚析构函数。类有虚函数会存在代价的
①移植困难
看下面代码
class Point
{
public:
	Point(const int x, const int y) : m_x(x), m_y(y)
	{

	}
	~Point() {}
private:
	int m_x;
	int m_y;
};
调用,赋给C语言结构体
	Point point(4, 5);

	typedef struct 
	{
		int x;
		int y;
	} Point_C; //c语言结构体

	Point_C *pc = (Point_C *)&point;

	cout<<"X:"<<pc->x<<" Y :"<<pc->y<<endl;
调用结果,是我们想得到的
X:4 Y :5

将析构函数改成虚析构函数后
virtual ~Point() {}
同样调用结果
X:3359148 Y :4
看到的结果,X的值是一个指针的地址值(vptr,下面详说), Y是第二个值。
②增大内存开销
       一个class存在虚函数,那些编译器为该类生成一个vptr(虚函数指针),占用内存4 byte,vptr主要是指向本类中的虚函数地址列表(vtbl),是一个数组,每个元素存放虚函数地址。vptr在类中空间部署,有些编译器是放在开头,如上面X就是虚函数地址,有些是放在后面。
       如果系统中存在很多这样的函数,那会额外占用很大的内存,特别是对一些内存有限制的系统。

3、纯虚析构函数

    抽象类
        抽象类不能够实例化对象,只能通过指针指向derived class对象,实现多态性机制。但抽象类的定义需要有一个pure 虚函数,如果一个abstract class类中没有其他函数当做pure 虚函数,那么令析构函数为pure virtual 函数是一个良好的做法,这样该类既有虚析构函数,又可以将其抽象。
将TimeKeeper类定义为抽象类可如下,注意一点:纯虚析构函数必须定义,因为析构函数的运作方式是:先调用最深层的derived class析构函数,最后调用base class析构函数,如果不定义,则连接器会报错
	virtual ~TimeKeeper() = 0
	{
		cout<<"TimeKeeper destruction\n";
	}
并非所有的base类都用来设计成多态性,如实现对象的继承性,后续会详细说明

记住:
1、polymorphic(带多态性质的) base classes应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
2、Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性质(polymorphically),就不该声明virtual析构函数。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值