《Effective C++》学习笔记(条款07:为多态基类声明virtual析构函数)

《Effective C++》学习笔记(条款07:为多态基类声明virtual析构函数),第1张

《Effective C++》学习笔记(条款07:为多态基类声明virtual析构函数)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!

1 多态的基类需要虚构函数

 class TimeKeeper{                              //计时器类,用来当做基类
 public:
     TimeKeeper();                              //这是构造函数
     ~TimeKeeper();                             //这是析构函数
   ......
 };
 
 class AtomicClock : public TimeKeeper{...};   //原子钟是一种计时器
 class WaterClock : public TimeKeeper{...};    //水钟也是一种计时器
 
 TimeKeeper* getTimeKeeper(){...}              //用来返回一个动态分配的基类对象,工厂类
 TimeKeeper* ptk = getTimeKeeper();            //使用这个基类指针 *** 作它的子类,假设这个子类是WaterClock
 .....                                         
 delete ptk;                                   //使用完毕,释放资源

上述代码的基类没有虚析构函数,直接 delete 基类指针指向的派生类(delete ptk;),会导致派生类 WaterClock 中的基类成分( 即 TimeKeeper 的成员变量)被释放,而专属于派生类 WaterClock 内的成员变量没被释放,且它的析构函数也未能执行,于是造成了一个诡异的局部销毁对象,这也是造成内存泄漏、败坏数据结构、在调试器上浪费时间的原因之一。

解决方法:给多态的基类的析构函数添加 virtual 关键字

 class TimeKeeper{           //计时器类,用来当做基类
 public:
     TimeKeeper();          	//这是构造函数
     virtual ~TimeKeeper();	//这是虚析构函数
   ......
 };

此后 delete 基类指针指向的派生类,派生类中的所有成员变量都会被释放。

2 虚函数的工作原理

虚函数是用来在运行时(runtime),自动把编译时未知的对象,比如用户输入的对象,和它所对应的函数绑定起来并调用。当一个类中有虚函数时,编译器会给这个类添加一个隐藏变量 vptr,即虚函数表指针(virtual table pointer),vptr 指向一个由函数指针构成的数组 vtbl,即虚函数表(virtual table)。当对象调用某一虚函数时,具体调用哪个函数取决于该对象的 vptr 所指的那个 vtbl——编译器在其中寻找适当的函数指针。

如果类内有虚函数,其对象的体积会增加,因为 vptr 、vtbl都需要占用空间的,所有不要无谓声明虚析构函数。

3 不要继承标准库中的类

 class SpecialString : public std::string{...};      //某个继承自标准字符串的类
 
 SpecialString* pss = new SpecialString("Hi");
 std::string* ps;
 ...
 ps = pss;
 delete ps;                                          //使用完后从基类删除内存

这样的写法会导致内存泄漏,因为标准库的字符串并没有把析构函数定义为虚函数,它们并不是用来拿去继承的,所以不能随便继承,包括 STL。虽然C++不像Java有final和C#有 sealed 来阻止某些类被继承的机制,我们也要拒绝这种写法。

4 抽象类

抽象类是包含至少一个纯虚函数的类,而且它们不能被实例化,只能通过指针来 *** 作,是纯粹被用来当做多态的基类的。

因为多态的基类需要有虚析构函数,抽象类又需要有纯虚函数,那么在抽象类中就要把析构函数声明为纯虚函数,如下述代码

 class AWOV{
   public:
     virtual ~AWOV() =0; //"=0"只是一个关键字,用来声明纯虚函数,并不把任何东西设为0
 };
 
 AWOV::~AWOV(){
     //纯虚析构函数的定义
 }

Note

  • 带多态性质的基类应该声明一个虚析构函数;如果 class 内至少有一个虚函数,应该为它声明虚析构函数
  • 不是所有基类都需要声明虚析构函数的,如条款06中的 Uncopyable 类,只是为了实现一个功能,而不具有多态性质。

条款08:别让异常逃离析构函数

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5115475.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-17
下一篇 2022-11-17

发表评论

登录后才能评论

评论列表(0条)

保存