私有地继承具有非虚拟析构函数的类是否安全?

时间:2012-09-17 17:51:40

标签: c++ inheritance destructor

我理解,对于公共继承,它通常是安全的,因为当delete基类指针时,编译器只生成调用基类的析构函数的代码,以及派生类的一个人没有被召唤。

但是对于私有继承,客户端无法将派生类指针强制转换为基类指针(因为私有继承不会模拟 is-a 关系),因此始终使用delete在派生类的指针上,编译器应该能够看到还有一个基类并调用它的析构函数。

我做了这个测试:

#include <iostream>

struct BaseVirtual
{
    virtual ~BaseVirtual()
    {
        std::cout << "BaseVirtual's dtor" << '\n';
    }
};

struct BaseNonVirtual
{
    ~BaseNonVirtual()
    {
        std::cout << "BaseNonVirtual's dtor" << '\n';
    }
};

struct DerivedPrivVirtual: private BaseVirtual
{
    static void f()
    {
        BaseVirtual * p = new DerivedPrivVirtual;
        delete p;
    }

    ~DerivedPrivVirtual()
    {
        std::cout << "DerivedPrivVirtual's dtor" << '\n';
    }
};

struct DerivedPrivNonVirtual: private BaseNonVirtual
{
    static void f()
    {
        BaseNonVirtual * p = new DerivedPrivNonVirtual;
        delete p;
    }

    ~DerivedPrivNonVirtual()
    {
        std::cout << "DerivedPrivNonVirtual's dtor" << '\n';
    }
};

int main()
{
    std::cout << "With explicit derived pointer type:" << '\n';
    {
        DerivedPrivVirtual * derivedPrivVirtual = new DerivedPrivVirtual;
        DerivedPrivNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual;

        delete derivedPrivVirtual;
        delete derivedPrivNonVirtual;
    }
    std::cout << '\n';

    std::cout << "With base pointer type:" << '\n';
    {
        // Client code can't cast Derived to Base when inherit privately.
        //BaseVirtual * derivedPrivVirtual = new DerivedPrivVirtual;
        //BaseNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual;

        //delete derivedPrivVirtual;
        //delete derivedPrivNonVirtual;
    }
    std::cout << '\n';

    std::cout << "Inside derived class itself:" << '\n';
    {
        DerivedPrivVirtual::f();
        DerivedPrivNonVirtual::f();
    }
    std::cout << '\n';

    std::cout << "With non-dynamic variables:" << '\n';
    {
        DerivedPrivVirtual derivedPrivVirtual;
        DerivedPrivNonVirtual derivedPrivNonVirtual;
    }
    std::cout << '\n';
}

GCC 4.7.1和CLang 3.1都提供相同的输出。调用派生类构造函数,除非派生类本身将派生类指针强制转换为基类并且delete为它。

除了这个看起来非常罕见且容易避免的案例(班级的作者是唯一可以造成伤害的人,但它确实知道从哪个派生出来的那个人),我能否得出结论它是安全的?

With explicit derived pointer type:
DerivedPrivVirtual's dtor
BaseVirtual's dtor
DerivedPrivNonVirtual's dtor
BaseNonVirtual's dtor

With base pointer type:

Inside derived class itself:
DerivedPrivVirtual's dtor
BaseVirtual's dtor
BaseNonVirtual's dtor  <-- Only a problem inside the class itself

With non-dynamic variables:
DerivedPrivNonVirtual's dtor
BaseNonVirtual's dtor
DerivedPrivVirtual's dtor
BaseVirtual's dtor

奖金问题:保护继承怎么样?我认为做损害的能力不再是直接派生类作者的特权,而是层次结构中任何类的作者。

2 个答案:

答案 0 :(得分:10)

继承是公共还是私有不会影响代码的安全性,它只会限制安全/不安全使用的范围。您有相同的基本问题:如果您的类或您的类的朋友将您的类型的对象传递给一个接口,该接口在没有虚拟析构函数的情况下获取指向基础的指针,并且该接口获得对象的所有权,那么您创建的是未定义的行为。

设计中的问题是,根据您的问题,BaseNonVirtual并非旨在扩展。如果是,它应该具有公共虚拟析构函数或受保护的非虚拟析构函数,确保没有代码能够通过指向基础的指针在派生对象上调用delete。

答案 1 :(得分:1)

有一种情况是客户端代码可以将Derived转换为Base,尽管有私有继承:

delete reinterpret_cast<BaseNonVirtual*>(new DerivedPrivNonVirtual);

因此跳过~DerivedPrivNonVirtual()的执行。

但是,考虑到不鼓励使用reinterpret_cast多少,你可以得出结论:它足够安全&#34;为了你的目的。