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析构函数。
  • 相关阅读:
    Graph 学习
    忘记 mysql 8.0 root 密码 怎么修改
    HTML字符实体(关于 ><等)
    Sqlserver 中系统表sysobjects、syscolumns以及函数object_id
    SQL Server创建索引(转)
    改善SQL语句(转)
    (转)SqlServer索引及优化详解(1)
    标签点击不返回顶部和不刷新页面方法
    c#模拟js escape方法
    获取枚举描述信息
  • 原文地址:https://www.cnblogs.com/zhonghuasong/p/7353501.html
Copyright © 2011-2022 走看看