15

基本クラスのポインターを ing するとき、コンパイラーは基本クラスのデストラクタを呼び出すコードのみを生成し、派生クラスのデストラクタは呼び出されないため、パブリック継承では一般に安全ではないことを理解しています。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それを s する場合を除き、呼び出されます。

非常にまれで簡単に回避できるこのケース (クラスの作成者は害を及ぼすことができる唯一の人ですが、どのクラスから派生したかを知っています) のほかに、安全であると結論付けることができますか?

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

おまけの質問: 保護された継承についてはどうですか? 危害を加える能力は、直接派生したクラスの作成者にもはや特権ではなく、階層内の任意のクラスの作成者に特権があると思います。

4

2 に答える 2

10

継承がパブリックかプライベートかに関係なく、コードの安全性には影響しません。安全/非安全に使用できる範囲が制限されるだけです。同じ基本的な問題があります。クラスまたはクラスの友人が、仮想デストラクタなしでベースへのポインターを受け取るインターフェイスにタイプのオブジェクトを渡し、そのインターフェイスがオブジェクトの所有権を取得した場合、undefined を作成しています。行動。

設計の問題は、質問によると、BaseNonVirtual拡張するように設計されていないことです。そうである場合は、パブリック仮想デストラクタ、または保護された非仮想デストラクタのいずれかが必要であり、ベースへのポインタを介して派生オブジェクトでコードが削除を呼び出せないことを保証します。

于 2012-09-17T18:01:12.133 に答える