Effective C++ 07:为多态基类声明virtual析构函数
2016-03-01 21:27
411 查看
先上结论:
1、带有多态性质的基类,应该声明一个 virtual 析构函数。如果类带有任何 virtual 函数,它就应该有一个 virtual 析构函数。
2、类的设计目的如果不是作为 base class 使用,或不是为了具备多态性,就不该声明 virtual 析构函数。
举一个工厂模式的例子:
先定义一个基类 TimeKeeper 和它的派生类 AtomicClock 和 WristWatch,设计一个工厂函数(工厂模式),返回指针指向一个计时对象。
在该函数中,我们 new 了一个指针,为了防止内存泄露,在使用完毕的时候一定要delete:
“依赖客户端执行 delete 动作,基本上便带有某种错误的倾向”!
如何理解这句话呢?getTimeKeeper 函数返回的指针类型是基类的,但指针却是指向一个派生类的对象,并最终要经由一个基类指针被删除,基类的析构函数是 non-virtual 的,这就导致在实际的执行中,对象中基类的部分被析构,但是派送类独有的部分却没有!这种情况被称为“局部销毁”对象,很容易造成内存泄露。
所以我们应该将基类的析构函数声明为 virtual 函数: virtual ~TimeKeeper(); 确保在调用析构函数的时候,子类的析构函数可以“覆盖”父类的析构函数,从而完整的析构整个对象。
但是,如果一个类,在设计的时候并不是为了作为 base class 来使用,或着说不是为了具备多态性,那么就不该将析构函数声明为 virtual 函数(这种情况下其他的成员函数也都不应该被声明为 vi
9ec5
rtual 函数)。
这是从代码的可移植性角度总结的做法。
这样一个类,它的一个对象可以放入一个64位的寄存器,或者作为一个“64位长度的量”传递。
但如果这个类有一个 virtual 函数,那么它的对象必须携带更多的信息,用来在运行时期决定哪一个 virtual 函数该被调用。为了保存这些信息,我们需要一张虚表(vtbl)来保存函数的指针,相应的,类 Point 里要增加一个虚表指针 vptr
来指向这张虚表。这样的话,类需要的空间就是 96bits,那么在传递类的对象的时候,就不能把它当做“64bits的量”,要新增很多细节的实现。因此不再具有移植性。
所以,如果一个类不需要具备多态性,那么就不要为它声明 virtual 函数。
1、带有多态性质的基类,应该声明一个 virtual 析构函数。如果类带有任何 virtual 函数,它就应该有一个 virtual 析构函数。
2、类的设计目的如果不是作为 base class 使用,或不是为了具备多态性,就不该声明 virtual 析构函数。
举一个工厂模式的例子:
class TimeKeeper {
public:
TimeKeeper();
~TimeKeeper();
};
class AtomicClock : public TimeKeeper
{
//原子钟
};
class WristWatch : public TimeKeeper
{
//腕表
};
先定义一个基类 TimeKeeper 和它的派生类 AtomicClock 和 WristWatch,设计一个工厂函数(工厂模式),返回指针指向一个计时对象。
TimeKeeper* getTimeKeeper()
{
TimeKeeper* t_keeper = NULL;
switch(type)
{
case 1:
t_keeper = new AtomicClock();
break;
case 2:
t_keeper = new WristWatch();
break;
default:
break;
}
return t_keeper;
}
在该函数中,我们 new 了一个指针,为了防止内存泄露,在使用完毕的时候一定要delete:
“依赖客户端执行 delete 动作,基本上便带有某种错误的倾向”!
如何理解这句话呢?getTimeKeeper 函数返回的指针类型是基类的,但指针却是指向一个派生类的对象,并最终要经由一个基类指针被删除,基类的析构函数是 non-virtual 的,这就导致在实际的执行中,对象中基类的部分被析构,但是派送类独有的部分却没有!这种情况被称为“局部销毁”对象,很容易造成内存泄露。
所以我们应该将基类的析构函数声明为 virtual 函数: virtual ~TimeKeeper(); 确保在调用析构函数的时候,子类的析构函数可以“覆盖”父类的析构函数,从而完整的析构整个对象。
但是,如果一个类,在设计的时候并不是为了作为 base class 来使用,或着说不是为了具备多态性,那么就不该将析构函数声明为 virtual 函数(这种情况下其他的成员函数也都不应该被声明为 vi
9ec5
rtual 函数)。
这是从代码的可移植性角度总结的做法。
class Point {
public:
Point();
~Point();
private:
int32_t a, b;
};
这样一个类,它的一个对象可以放入一个64位的寄存器,或者作为一个“64位长度的量”传递。
但如果这个类有一个 virtual 函数,那么它的对象必须携带更多的信息,用来在运行时期决定哪一个 virtual 函数该被调用。为了保存这些信息,我们需要一张虚表(vtbl)来保存函数的指针,相应的,类 Point 里要增加一个虚表指针 vptr
来指向这张虚表。这样的话,类需要的空间就是 96bits,那么在传递类的对象的时候,就不能把它当做“64bits的量”,要新增很多细节的实现。因此不再具有移植性。
所以,如果一个类不需要具备多态性,那么就不要为它声明 virtual 函数。
相关文章推荐
- 1>XxxDlg.obj : error LNK2005: "char * * tabPageData" (?tabPageData@@3PAPADA) 已经在 XxxDlg.obj 中定义
- Function Run Fun(北大2015上机考试)
- [c++]继承的经典例子
- 《C语言程序设计(第二版新版)》第一章习题解答(部分)
- 【C/C++学院】0906-递归转栈/二叉树实现
- C语言main函数的参数及其返回值详细解析
- c++实验1-求两个数的和差
- C语言 数组
- C/C++ 中的0长数组(柔性数组)
- 算法代码实现之堆排序,C/C++实现
- Effective Modern c++ 条款总结
- 2015年蓝桥杯省赛B组C/C++(试题+答案)前几题
- 理解C语言——从小菜到大神的晋级之路(12)——动态内存管理
- C++16道面试题
- [土狗之路]coursera C语言进阶 习题 分配病房
- C++开发人脸性别识别总结
- C语言深度剖析-----多维数组和多维指针
- C语言深度剖析-----指针数组和数组指针的分析
- C语言深度剖析-----数组参数和指针参数分析
- C语言编译链接流程以及重要特性在编译器中实现