Effective C++ Item 7 为多态基类声明virtual析构函数



经验1:带多态性质的基类应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual 析构函数

示例:不为带多态性质的基类声明一个virtual析构函数

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class TimeKeeper  
  6. {  
  7. public:  
  8.     ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;}  
  9. };  
  10. class AtomicClock: public TimeKeeper{  
  11.     ~AtomicClock(){cout << "AtomicClock destructor << endl";}  
  12. };  
  13.   
  14. class WaterClock: public TimeKeeper{  
  15.     ~WaterClock(){cout << "WaterClock destructor << endl";}  
  16. };  
  17.   
  18. TimeKeeper* getTimeKeeper(string type){  
  19.     if(type == "AtomicClock"return new AtomicClock();  
  20.     else    return new WaterClock();  
  21. }  
  22.   
  23. int main(){  
  24.     TimeKeeper *tk = getTimeKeeper("AtomicClock");  
  25.     delete tk;  
  26.     system("pause");  
  27. }  
输出:

TimeKeeper destructor 

解析:

当derived class 对象经由一个base class 指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁,造成资源泄漏。

纠正:为带多态性质的基类声明一个virtual析构函数

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class TimeKeeper  
  6. {  
  7. public:  
  8.     virtual ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;} //这里多了个virtual  
  9. };  
  10. class AtomicClock: public TimeKeeper{  
  11.     ~AtomicClock(){cout << "AtomicClock destructor" << endl;}  
  12. };  
  13.   
  14. class WaterClock: public TimeKeeper{  
  15.     ~WaterClock(){cout << "WaterClock destructor << endl";}  
  16. };  
  17.   
  18. TimeKeeper* getTimeKeeper(string type){  
  19.     if(type == "AtomicClock"return new AtomicClock();  
  20.     else    return new WaterClock();  
  21. }  
  22.   
  23. int main(){  
  24.     TimeKeeper *tk = getTimeKeeper("AtomicClock");  
  25.     delete tk;  
  26.     system("pause");  
  27. }  

输出:

AtomicClock destructor

TimeKeeper destructor


经验2:classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual 析构函数

示例:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class Point1{  
  6. public:  
  7.     ~Point1(){};  
  8. private:  
  9.     int x, y;  
  10. };  
  11.   
  12.   
  13. class Point2{  
  14. public:  
  15.     virtual ~Point2(){};  
  16. private:  
  17.     int x, y;  
  18. };  
  19.   
  20. int main(){  
  21.     Point1 p1;  
  22.     Point2 p2;  
  23.     cout << sizeof(p1) << endl  
  24.         << sizeof(p2) << endl;  
  25.   
  26.     system("pause");  
  27. }  

输出:

8

12

解析:

p1的size是8:两个int类型分别是4个字节,所以总共是8个字节

p2的size是12:两个int类型分别是4个字节,共8个字节;有一个virtual函数,所以对象要携带一个虚表指针vptr(virtual table pointer),vptr指向一个由函数指针构成的数组,称为vtbl(virtual table),用来在运行期决定哪一个virtual 函数该被调用。具体关于虚表指针的问题可参见陈皓的博客( http://blog.csdn.net/haoel/article/details/1948051  )。

由输出结果可见,无端地将所有classes的析构函数声明为virtual,会增加class的对象的存储空间。因此只有当class内含有至少一个virtual函数时,才要将析构函数声明为virtual


经验3:不要企图继承一个标准容器或者其他包含“non-trivial 析构函数”的class,例如string, vector, list, set 等

示例:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class SpecialString: public string{  
  6. public:  
  7.     ~SpecialString(){cout << "SpecialString destructor" << endl;}  
  8. };  
  9.   
  10. int main(){  
  11.     SpecialString *pss = new SpecialString();  
  12.     string *ps = pss;  
  13.     delete ps;  
  14.     system("pause");  
  15. }  

输出:

  (空)

 解析:

 标准string 不含任何virtual函数,delete指向SpecialString类型的指针,只会调用string类的析构函数,不会调用SpecialString的析构函数,现实中*ps的SpecialString资源会泄漏。这跟经验1其实是一样道理。

经验1:带多态性质的基类应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual 析构函数

示例:不为带多态性质的基类声明一个virtual析构函数

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class TimeKeeper  
  6. {  
  7. public:  
  8.     ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;}  
  9. };  
  10. class AtomicClock: public TimeKeeper{  
  11.     ~AtomicClock(){cout << "AtomicClock destructor << endl";}  
  12. };  
  13.   
  14. class WaterClock: public TimeKeeper{  
  15.     ~WaterClock(){cout << "WaterClock destructor << endl";}  
  16. };  
  17.   
  18. TimeKeeper* getTimeKeeper(string type){  
  19.     if(type == "AtomicClock"return new AtomicClock();  
  20.     else    return new WaterClock();  
  21. }  
  22.   
  23. int main(){  
  24.     TimeKeeper *tk = getTimeKeeper("AtomicClock");  
  25.     delete tk;  
  26.     system("pause");  
  27. }  
输出:

TimeKeeper destructor 

解析:

当derived class 对象经由一个base class 指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁,造成资源泄漏。

纠正:为带多态性质的基类声明一个virtual析构函数

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class TimeKeeper  
  6. {  
  7. public:  
  8.     virtual ~TimeKeeper(){cout << "TimeKeeper destructor" << endl;} //这里多了个virtual  
  9. };  
  10. class AtomicClock: public TimeKeeper{  
  11.     ~AtomicClock(){cout << "AtomicClock destructor" << endl;}  
  12. };  
  13.   
  14. class WaterClock: public TimeKeeper{  
  15.     ~WaterClock(){cout << "WaterClock destructor << endl";}  
  16. };  
  17.   
  18. TimeKeeper* getTimeKeeper(string type){  
  19.     if(type == "AtomicClock"return new AtomicClock();  
  20.     else    return new WaterClock();  
  21. }  
  22.   
  23. int main(){  
  24.     TimeKeeper *tk = getTimeKeeper("AtomicClock");  
  25.     delete tk;  
  26.     system("pause");  
  27. }  

输出:

AtomicClock destructor

TimeKeeper destructor


经验2:classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual 析构函数

示例:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class Point1{  
  6. public:  
  7.     ~Point1(){};  
  8. private:  
  9.     int x, y;  
  10. };  
  11.   
  12.   
  13. class Point2{  
  14. public:  
  15.     virtual ~Point2(){};  
  16. private:  
  17.     int x, y;  
  18. };  
  19.   
  20. int main(){  
  21.     Point1 p1;  
  22.     Point2 p2;  
  23.     cout << sizeof(p1) << endl  
  24.         << sizeof(p2) << endl;  
  25.   
  26.     system("pause");  
  27. }  

输出:

8

12

解析:

p1的size是8:两个int类型分别是4个字节,所以总共是8个字节

p2的size是12:两个int类型分别是4个字节,共8个字节;有一个virtual函数,所以对象要携带一个虚表指针vptr(virtual table pointer),vptr指向一个由函数指针构成的数组,称为vtbl(virtual table),用来在运行期决定哪一个virtual 函数该被调用。具体关于虚表指针的问题可参见陈皓的博客( http://blog.csdn.net/haoel/article/details/1948051  )。

由输出结果可见,无端地将所有classes的析构函数声明为virtual,会增加class的对象的存储空间。因此只有当class内含有至少一个virtual函数时,才要将析构函数声明为virtual


经验3:不要企图继承一个标准容器或者其他包含“non-trivial 析构函数”的class,例如string, vector, list, set 等

示例:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4.   
  5. class SpecialString: public string{  
  6. public:  
  7.     ~SpecialString(){cout << "SpecialString destructor" << endl;}  
  8. };  
  9.   
  10. int main(){  
  11.     SpecialString *pss = new SpecialString();  
  12.     string *ps = pss;  
  13.     delete ps;  
  14.     system("pause");  
  15. }  

输出:

  (空)

 解析:

 标准string 不含任何virtual函数,delete指向SpecialString类型的指针,只会调用string类的析构函数,不会调用SpecialString的析构函数,现实中*ps的SpecialString资源会泄漏。这跟经验1其实是一样道理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值