一个虚析构函数引发的讨论

来源:互联网 发布:linux 卸载chromium 编辑:程序博客网 时间:2024/05/17 02:37

今天同事帮我作code review的时候,提了一个comment: 为什么这个类的析构函数前面不加上virtual, 防止多态析构的时候父类析构函数没有被调到?

我解释说是因为这个类没有其他的虚函数,也没打算让其他类来继承它,如果非要在析构函数前面加个virtual, 这个类还要维护一个虚函数表指针,有点画蛇添足。

接着同事的回答也很有道理:万一这个类被继承了,又刚好是用父类指针的方式析构呢?

我当时的第一反应是:那不是所有C++的类都要加个析构函数了? 后来反应过来,能不能利用一些手段,把这个类限制住,不允许它被继承?

在JAVA中有一个关键字:final, 被final修饰的类或方法不能被用来继承。那C++呢?查了下资料,原来在C++11中,也引入了final关键字,来达到同样的目地。

但是我们这个项目是运行在老版本的C++上的,这又怎么办?

我们知道,派生类对象实例化时,首先要调用基类构造函数,对基类部分对象进行初始化,之后再调用派生类构造函数,对派生类部分的对象进行初始化。那我们能不能像单件那样,对基类的构造函数动一些手脚,来防止它被派生类调用?

后来果然在Stackoverflow上找到了下面的一种方法:

class CSealed {    protected:        CSealed() {}};class Base : virtual CSealed {    public:        Base() {}        ~Base() {cout << "Base::~Base()" << endl;}};class Derive : public Base {    public:        Derive() {cout << "derive" << endl;}};

在上面的代码中,class Derive试图继承class Base时,会报编译错误:
error: ‘CSealed::CSealed()’ is protected
从而我们达到了禁止class Base被继承的目的。

为什么Derive会因为CSealed的原因,不能继承Base呢?因为这里利用的虚继承的机制,使得Derive必须要跨过Base,直接去调用CSealed的构造函数,但是又因为CSealed的构造函数是protected的,Derive没有调用权限,从而导致了编译出错。
又由于Base是继承自CSealed的,因此Base对CSealed的构造函数有访问权限。

最后总结一下,这个问题本身就比较tricky。其根源是C++在多态情况下,对类对象进行析构时,不会调用其父类的非虚析构函数陷阱。为了避免踩进这个陷阱,我们又做了一些workaround来在编译器的范围限制它,这也难怪在C++11中会引入final关键字了。

1 0
原创粉丝点击