• 条款07:为多态基类声明虚析构函数


    看下面一个类:

    class TimeKeeper{

    public :

         TimeKeeper();

         ~TimeKeeper();

         ....

    };

    class AtomicClock : public TimeKeeper{...};

    class WaterClock : public TimeKeeper{...};

    class WristClock : public TimeKeeper{...};

             当我们使用时,我们可以采用一个工厂方法,用基类的指针指向派生类的对象:

             TimeKeeper * getTimeKeeper();

            

    TimeKeeper * ptk = getTimeKeeper(); //用基类的指针指向派生类的对象

                       ....

    delete ptk;

     

    (注,在以后,我会说明这种依赖用户来delete的方法不是一个好方法。但是现在不是我们所要讨论的问题。我们要讨论的问题是:。。。。)

    我们要讨论的是,当我们把ptk进行析构时,会发生什么事情?

     

    由于ptk是一个基类的指针,当进行析构时,会调用基类的析构函数,也就是说会把ptk所指对象的基类部分进行,但是ptk实际指向了一个派生类的对象,对于派生类的数据怎么办呢?此时会产生不明确行为,这是造成内存泄露的一个绝佳方式。

     

    如果我们把基类的析构函数做成虚函数,则这个问题就可以解决了:

             class TimeKeeper{

    public :

         TimeKeeper();

         virtual ~TimeKeeper();

         ....

    };

    class AtomicClock : public TimeKeeper{...};

    class WaterClock : public TimeKeeper{...};

    class WristClock : public TimeKeeper{...};

            

             由于基类的析构函数是一个虚函数,则每一个派生类中的析构函数都是虚函数。当我们再次使用一个基类指针指向派生类的时候,如下:

    TimeKeeper * ptk = getTimeKeeper();

    ....

    delete ptk;

    再次进行析构时,由于析构函数是一个虚函数,所以在析构时,会在运行期来决定调用哪个类的析构函数,此时,经判断是派生类的析构函数,所以会调用派生类的析构函数,此时,可以把ptk所指向的对象全部析构。

     

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

     

           如果一个class不含virtual函数,通常表示它并不意图被用做一个base class,即使被当作基类,也不希望用基类的指针来指向一个派生类的对象。当class不企图被当作base class,令其析构函数为virtual,往往不是一个好主意。如下类:

    class Point{

    public :

         Point(int xCoord, int yCoord) : x(xCoord),y(yCoord){}

         ~Point();

    private:

         int x, y;

    }

    这样一个类的对象,会占用64个字节的内存,如果其析构函数是一个虚函数,则此类的对象,将会是96字节,这样,浪费了50%的空间。

    所以许多人的心得是:只有当class内含至少一个virtual函数,才为它声明一个virtual析构函数。

     

             请记住:

    • Polymorphic base class 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
    • Class的设计目的如果不是作为base class 使用,或不是为了具备多态性,就不应该声明virtual析构函数。s
  • 相关阅读:
    go第二天
    go第一天
    engish
    english
    git 生成公钥
    tp5 验证码
    css处理文本折行截断
    数组对象总结(牢记)
    全局css样式
    Flexbox 弹性盒子布局
  • 原文地址:https://www.cnblogs.com/loveyakamoz/p/2772377.html
Copyright © 2020-2023  润新知