如果没有将多态基类的析构函数声明为virtual
当通过基类指针构造子类对象的时候,此时使用基类来引用/表示子类:
BaseClass *b = new ChildClass;
如果析构函数不是virtual的,那么调用析构函数时:
delete b;
这时,调用BaseClass的析构函数,进行析构;==问题出现:使用基类的析构函数进行析构操作,但是实际对象却是这个基类的子类。所以,这样析构只能释放基类的部分,而子类的部分没有被释放(因为基类的析构函数并没有能力去析构子类的成员部分),造成内存泄露。==
将多态基类的析构函数声明为virtual的目的
继续上面的例子,但是将BaseClass的析构函数定义为virtual。这时,delete b
仍然通过子类BaseClass来调用析构函数。==但是,此时析构函数是virtual的,并且基类变量实际指向的是其子类对象,因此,发生动态绑定。==动态绑定的结果是,delete
调用的析构函数实际是子类的析构函数。这样,通过基类进行的析构操作成功地通过动态绑定调用了子类的析构函数,能够将子类的部分析构释放掉,避免内存泄露。
总结:将析构函数声明为virtual是为了激活动态绑定,用对应子类的析构函数来执行析构操作
任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数
当一个对象不存在virtual函数时,往往表示它并不希望自己成为基类,并不希望被继承
当一个类不会被继承的时候,让它的析构函数成为virtual是不良的设计,因为会带来额外的开销和无用处的virtual标记
virtual要求对象携带一些额外的信息(包括Virtual Table Pointer),这些信息用来进行动态绑定。当一个类不会被继承,动态绑定根本对它没有任何作用且带来额外开销,因此不应该强行套一个virtual。
除了额外开销外,==virtual函数还会使对象的内部组织结构发生变化,这个变化导致这个对象不可能与C语言兼容==。(例:如果一个对象内只有一个int而没有其它东西,那么这个对象可以装在一个int空间内并以in表示,这个对象可以被C使用。)
总结:当class内包含virtual时,就把析构函数声明为virtual,否则不要声明为virtual析构
string的析构函数是non-virtual的,如果从string派生自己的类,然后用string来delete这个类,这时调用的就是string的析构函数,而不是自己派生的类的析构函数,这时就会导致部分内容未析构,导致行为未定义,属于程序事故。
纯虚析构函数virtual ~Foo() = 0;可以确保动态绑定调用子类的析构函数
纯虚函数不能被调用,且要求子类进行override,因此,能够确保动态绑定调用子类析构函数。
注意:纯虚函数所在的类是纯虚类
本人公众号:taojuxiu