C++人该知道的N个问题与做法:为以实现多态的基类声明虚析构函数

前言--问题所在

假设我们需要记录颜色,因此设计一个Color基类和一些派生类:

class Color{
public:
    Color();
    ~Color();
};
class Red: public Color {...};
class Green : public Color {...};
class Blue: public Color {...};

有时我们只想操作对应的颜色,但不操心其内部的计算,通常会用指针指向一个Color对象;

Color* getColor();  //返回指针,指向一个Color子类的动态分配对象

函数会”返回一个基类指针,指向新生成的子类对象“;

为了避免内存泄漏和其他资源,我们要将函数返回的每一个对象都适当的delete掉:

Color* pCol = getColor();
delete pCol;

依赖使用者去执行delete动作,通常是不好的,也会伴随着错误倾向,万一使用者忘记delete就完蛋了,纵使使用者每一件事都做对了,仍然没办法知道程序是如何运行的。

然而问题出在 Color返回的指针指向一个派生类对象(例如Red),而那个对象却经由一个基类指针被删除,而目前的基类没有虚析构函数。这将引来一个问题,因为C++明确指出,当派生类对象经由一个基类指针被删除,而该基类没有虚析构函数,实际执行时通常发生的是对象的派生部分没被销毁。如果 getColor返回指针指向一个Red对象,其内的 Red成分(也就是声明于Red类内的成员变量)很可能没被销毁,而 Red的析构函数也未能执行起来。然而其基类Color部分通常会被销毁,于是造成一个诡异的“局部销毁”对象的结果。这就是形成资源泄漏、数据结构损坏、在调试器上浪费许多时间的罪魁祸首。

消除“恐惧”的最好方法

消除这个问题的做法很简单:给基类添加一个虚析构函数,此后就会销毁整个对象,包括所有派生的部分:

class Color{
public:
    Color();
    virtual ~Color();
};
Color* pCol = getColor();
delete pCol;        //行为正确

任何 class只要带有 virtual函数都几乎确定应该也有一个 virtual析构函数。

其他:

如果 class不含 virtual函数,通常表示它并不意图被用做一个基类。当不企图被当作基类,令其析构函数为 virtual往往是个馊主意。考虑一个用来表维空间点坐标的类:

class Point {     //ー个二维空间点
public:
    Point(int xcoord, int ycoord);
    ~Point();
private:
    int x,y;
};

如果int占用32bits,那么 Point对象可塞入一个64-bit的缓存器中(x,y两个int)。更有甚者,这样一个 Point对象可被当做一个“64-bit量”传给以其他语言如C或 FORTRAN撰写的函数。然而当 Point的析构函数是 virtual,形势就变了。

了解虚函数的大家都知道:

欲实现 virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数该被调用。这份信息通常是由一个所谓vpr( virtual table pointer)指针指出,vpt指向一个由函数指针构成的数组,称为vtbl( virtual table),每一个带有 virtual函数的 class 都有一个相应的vtbl,当对象调用某一 virtual函数,实际被调用的函数取决于该对象的vpr所指的那个vtbl---编译器在其中寻找适当的函数指针。

virtual函数的实现细节不重要。重要的是如果 Point class内含 virtual函数,其对象的体积会增加:在32-bit计算机体系结构中将从占用64bits(为了存放两个int)变为96bits(两个int,加上一个vptr);在64bit计算机体系结构中可能占用64-128bits,因为指针在这样的计算机结构中占64bits,因此,为 Point添加一个vptr会增加其对象大小达50%~100%, Point对象不再能够塞入一个64bit缓存器,而C++的 Point对象也不再和其他语言(如C)内的相同声明有着一样的结构(因为其他语言的对象并没有vptr),因此也就不再可能把它传至(或接受自)其他语言所写的函数,除非你明确补偿vptr----那属于实现细节,也因此不再具有移植性。

所以,人们的心得是:只有当类中至少有一个虚函数时,才为类声明虚析构函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿尔兹

如果觉得有用就推荐给你的朋友吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值