2

問題のシナリオは次のとおりです。オブジェクトが破棄される直前に呼び出す必要のあるCleanup()仮想メソッドを含むC++オブジェクトがあります。正しいことを行うために、このCleanupメソッドは完全に形成されたオブジェクト(サブクラスデータを含む)にアクセスする必要があるため、自分のクラスのデストラクタの開始時にCleanup()を呼び出すことはできません。デストラクタが呼び出され、サブクラスがすでに完了しているデストラクタは、Cleanup()が確認する必要のあるデータをすでに解放している可能性があります。

明らかな解決策は、オブジェクトを削除する前に、呼び出し元のコードに手動でルーチンを呼び出すように要求することです。

theObject->Cleanup();
delete theObject;

しかし、遅かれ早かれ誰か(おそらく私)がCleanup()を呼び出すのを忘れ、悪いことが起こるので、その解決策は壊れやすいです。

別の解決策は、クラスをラップするpImplテクニックを実装する「ホルダー」オブジェクトを用意し、オブジェクトを削除する前に、ホルダーオブジェクトのデストラクタがオブジェクトに対してCleanup()を呼び出すようにすることです。ただし、このソリューションは、クラスの動作が標準のC ++クラスとは異なり、スタック上または静的にオブジェクトを割り当てることができないため、100%望ましいものではありません。

したがって、問題は、最初のサブクラスデストラクタを呼び出す前に、オブジェクトで指定された(おそらく仮想)メソッドを自動的に呼び出すようにコンパイラに指示するために使用できる巧妙な手法(C++またはC++ 11のいずれか)がありますか?

(考えてみると、最後のサブクラスコンストラクターが完了した直後にInit()メソッドを自動的に呼び出すための同様のメカニズムも便利な場合があります)

4

5 に答える 5

6

Cleanup横方向の思考:メソッドを取り除きます。それvirtualがデストラクタの目的です。

デストラクタの目的は、実行virtual時に最も外側の派生クラスのデストラクタを呼び出せるようにすることです。delete basepointer;RAII が示したように、クリーンアップデストラクタの仕事です。

Cleanupここで、デストラクタのタスクを容易にするため、またはオブジェクトを再利用するために、初期のメソッドが必要であると主張するかもしれません (Resetメソッドのようなものです)。実際には、それはすぐにメンテナンスの悪夢に変わります (1 つのフィールドをきれいにすることを忘れて、ある使用から別の使用に状態がリークするのはあまりにも簡単です)。

ですから、自問自答してください:作成された目的のためにデストラクタを使用しないのはなぜですか?

于 2012-06-01T06:22:43.680 に答える
3

PIMPLラッパーにひねりを加えることができます。

class PIMPL
{
    MyDataThatNeedsInitDestroy    object;

  public:
    PIMPL(Atgs a)
       : object(a)
    {
        object.postCreationInit();
    }

    ~PIMP()
    {
        object.preDestructionDestory();
    }

    // All other methods are available via -> operator
    MyDataThatNeedsInitDestroy* operator->() { return &object);
};
于 2012-06-01T03:59:42.343 に答える
1

はい、これは仮想継承で行うことができます。これは、仮想基本サブオブジェクトが常に最も派生した型によって構築 (および破棄) され、基本クラスの構築 (および破棄) の順序も明確に定義されているためです。

もちろん、delete演算子を非公開にして、でRelease()構成されるメンバー関数を提供することもできCleanup(); delete this; ますが、スタック割り当てオブジェクトでは役に立ちません。

于 2012-06-01T03:51:12.420 に答える
0
struct B
{
    void cleanup()
    {
        if (!m_bCleanedUp)
        {
            m_bCleanedUp = true;
            ...
        }
    }

    virtual B::~B()
    {
        assert(m_bCleanedUp);
        ...
    }

    bool m_bCleanedUp = false;
};

struct D : B
{
    D::~D()
    {
        cleanup(); // if D author forgets, assert will fire
        ...
    }
};
于 2012-06-01T04:06:03.377 に答える
-1

C++ クラスがデストラクタの実行を開始する直前にメソッドを自動的に実行するようにする方法はありますか?

生のポインターの代わりに、スマート ポインター (shared_ptr - boost または c++11 で使用可能) をカスタム デリーターと共に使用する必要があります。

typedef shared_ptr<MyClass> MyClassPtr;
class MyClassDeleter{
public:
    void operator()(MyClass* p) const{
        if (p)
            p->cleanup();
        delete p;
    }
};

...

MyClassPtr ptr(new MyClass, MyClassDeleter());

- 編集 -

これは、動的に割り当てられたオブジェクトに対して機能します。スタックに割り当てられたオブジェクトには (私が考えることができる) 解決策はありません。そのため、妥当なアクションは、コンストラクターをプライベートにし、構築されたオブジェクトに shared_ptr を返すフレンド ファクトリ メソッドを作成することです。このメカニズムが本当に必要であると仮定します。

于 2012-06-01T04:09:55.590 に答える