zoukankan      html  css  js  c++  java
  • Effective C++ 条款07:为多态基类声明virtual析构函数

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

    factory(工厂)函数

    class TimeKeeper {
    public:
    	TimeKeeper();
    	~TimeKeeper();
    	...
    };
    class AtomicClock: public TimeKeeper { ... };
    class WaterClock: public TimeKeeper { ... };
    class WristWatch: public TimeKeeper { ... };
    
    // factory 函数
    TimeKeeper* getTimeKeeper();   // 返回一个指针,指向一个TimeKeeper派生类的动态分配对象
    // 调用
    TimeKeeper* ptk = getTimeKeeper();  // 从TimeKeeper继承体系获得一个动态分配对象,这也叫做动态绑定。
    ...									 // 运用它
    delete ptk 							 // 释放它,避免资源泄漏
    

    问题来了,因为TimeKeeper的析构函数不是virtual的,那么在delete的时候,也就只能删除掉父类TimeKeeper,而不会掉用子类的析构函数,从而导致了内存泄漏。

    解决办法就是给base class一个virtual析构函数。

    class TimeKeeper {
    public:
    	TimeKeeper();
    	virtual ~TimeKeeper();
    	...
    };
    TimeKeeper* ptk = getTimeKeeper();  
    ...									 
    delete ptk   // 现在,行为正确
    

    注意 这个时候掉用析构函数的顺序是先掉用子类(derived class)的析构函数,再掉用父类的析构函数(base class),这个和构造函数的掉用顺序恰恰相反。

    规则二 如果class不含virtual函数,通常表示它并不意图被用做一个base class

    当class不企图被当作base class,令其析构函数为virtual往往是个馊主意。
    因为这样做会导致空间的浪费。
    浪费空间还好说,更严重的是可能导致内存泄漏。

    class SpecialString: public std::string {	// 馊主意!std::string有个non-virtual析构函数
    	...
    };
    // 调用
    SpecialString* pss = new SpecialString("Impending Doom");
    std::string* ps;
    ...
    ps = pss;			// SpecialString* => std::string*
    ...
    delete ps;			// 未有定义!现实中*ps的SpecialString资源会泄漏,因为SpecialString析构函数没被调用。
    

    相同的分析适用于任何不带virtua析构函数的class,包括所有STL容器如vector,list,set,trl::unordered_map等。

    规则三 有时候令class带一个pure virtual析构函数,可能颇为便利。

    纯虚函数会导致abstract classes,也就是不能被实体化的class,也就是说不能为这种类型创建对象。因此,想用的话,必须继承,继承又要考虑到析构的过程,所以我们考虑设置纯虚的析构函数就好了。
    关于虚函数和纯虚函数的区别请参考之前的博客。

    class AWOV {
    public:
    	virtual ~AWOV() = 0;
    };
    // 注意一定要定义纯虚析构函数,因为在析构的过程中,子类的析构函数会掉用父类的析构函数,所以必须定义,不然连接器会报错。
    AWOV::~AWOV() {}		// 纯虚析构函数的定义
    

    规则四 “给base class一个virtual析构函数” 这个规则只适用于带多态性质的基类上

    因为这种基类的设计目的就在于动态绑定,父类对象调用子类实例。
    并非所有的base class的设计目的都是为了多态用途。

    总结

    1. 带多态性质的base class应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
    2. classes的设计目的如果不是作为base classes使用,或者不是为了具备多态性质,就不该声明virtual析构函数。
  • 相关阅读:
    二叉查找树
    huffman coding
    普通树与二叉树
    递归转循环的通法
    尾递归和JAVA
    编译器和解释器
    开天辟地-用visualstudio2010编写helloworld
    Android app targetSdk升级到27碰到的一个bug补充说明
    Android Studio修改Apk打包生成名称
    Glide3升级到Glide4碰到的问题汇总以及部分代码修改
  • 原文地址:https://www.cnblogs.com/zhonghuasong/p/7353501.html
Copyright © 2011-2022 走看看