Item7:为多态基类声明virtual析构函数

文章讨论了在面向对象编程中,为何基类需要声明虚析构函数,特别是在涉及多态性和动态对象删除时。非虚析构函数可能导致对象的不完全销毁,而虚析构函数会增加对象的大小,影响效率。同时,纯虚析构函数可以用于创建抽象类,确保正确处理析构过程。
摘要由CSDN通过智能技术生成

0.概述

  • 带有多态性质的基类应该声明一个虚析构函数。如果类中带有任何一个虚函数,就应该拥有一个虚析构函数。
  • 类的设计目的如果不是为了作为基类,或不是为了具备多态性,就不该声明虚析构函数。

1.非虚析构函数带来的问题

设计一个TimeKeeper基类和一些派生类作为不同的计时方法:

class TimeKeeper {
    public:
    TimeKeeper();
    ~TimeKeeper();
...
};
class AtomicClock: public TimeKeeper { ... };
class WaterClock: public TimeKeeper { ... };
class WristWatch: public TimeKeeper { ... };

设计工厂(factory)函数,返回一个基类指针,指向新生成的派生类对象。

//返回一个指针,指向一个TimeKeeper派生类的动态分配对象
TimeKeeper* getTimeKeeper();

问题:getTimeKeeper返回的指针指向派生类对象,但那个对象却经由一个基类指针被删除,而目前的基类含有非虚析构函数。这有可能会导致对象的派生成分没有被销毁,造成一个”局部销毁“对象。

解决方案:给基类一个虚析构函数。

class TimeKeeper {
    public:
    TimeKeeper();
    virtual ~TimeKeeper();
...
};

通常如果类中带有任何一个虚函数,就应该拥有一个虚析构函数。

2.虚析构函数带来的问题

如果类中不含有虚函数,表示他不被用作一个基类,这时候不能将其析构函数设为虚函数。举例:

class Point { // 一个2D点
    public:
        Point(int xCoord, int yCoord);
        ~Point();
    private:
        int x, y;
};

从占用内存的角度考虑,如果int对象占用32bits,那么Points对象可以被放入一个64bits的缓存器中。

如果Point class 内含virtual函数,其对象的体积会增加:在32-bit计算机体系结构中将占用64 bits (为了存放两个ints)至96 bits(两个ints 加上 vptr)﹔在64-bit计算机体系结构中可能占用64~128 bits,因为指针在这样的计算机结构中占64 bits。

因此,为Point添加一个vptr会增加其对象大小达50%~1008!Point对象不再能够塞入一个64-bit缓存器,而C++的Point对象也不再和其他语言(如C)内的相同声明有着一样的结构(因为其他语言的对应物并没有vptr),因此也就不再可能把它传递至(或接受自)其他语言所写的函数,除非你明确补偿vptr—一那属于实现细节,也因此不再具有移植性。

3.禁止派生

有时程序员可能会错误地把不含任何虚函数的类当作基类,如标准string类;或任何不带虚析构函数的类,包括所有的STL容器如vector、list、set、tr1::unordered_map等。

4.纯虚析构函数,抽象类

纯虚函数会导致抽象类(不能被实体化,不能为其创建对象)。

可以为希望成为抽象类的那个类声明一个纯虚析构函数,如下:

class AWOV { 
    public:
        virtual ~AWOV() = 0; // 声明纯虚析构函数
};

这个类有一个纯虚析构函数,因此是个抽象类;由于有虚析构函数,所以不必担心析构函数的问题(完美闭环)。但是必须为这个纯虚析构函数提供一个定义。

AWOV::~AWOV(){ }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值