9

私は C# から来て、私の実践のいくつかを C++ に翻訳しようとしています。生のポインターを使用して、コード全体のさまざまな場所で依存性注入を使用しました。次に、生のポインターを std::shared_ptr のものに置き換えることにしました。そのプロセスの一環として、自動変数を動的に割り当てるのではなく、スタックに割り当てられた自動変数を使用することを検討することが提案されました (この質問を参照してください。その質問は unique_ptr のコンテキストにあったため、異なる可能性があります)。

以下の例は、自動変数の使用を示していると思います。

class MyClass
{ 
public:
   MyClass(ApplicationService& app): appService_(app)
   {
   }

   ~MyClass()
   {
        appService_.Destroy(something);

   }
private:   
   ApplicationService& appService_;
}

class ConsumerClass
{
    DoSomething()
    {
        CustomApplicationService customAppService;
        MyClass myclass(customAppService);
        myclass...
    }
}

上記の例で、customAppservice と myclass が範囲外になった場合、どちらが最初に破棄されるかをどうやって知ることができますか? customAppService が最初に破棄された場合、MyClass デストラクタは失敗します。これは、このシナリオで代わりに shared_ptr を使用する正当な理由ですか、それともこれを回避するクリーンな方法はありますか?

アップデート

ApplicationService は、コードが使用するサードパーティ ライブラリと対話するために必要なグローバル関数のラッパーであるクラスです。単体テストと独立した関数のスタブ/モックをサポートする標準的な方法であると信じているため、このクラスがあります。このクラスは、対応するグローバル関数への呼び出しを委任するだけです。呼び出し appService_.Destroy(something); MyClass の特定のインスタンスごとに使用されるオブジェクトを実際に破棄していますが、Application クラス自体とは何の関係もありません。

4

2 に答える 2

7

答えは次のとおりです。とにかく、設計が壊れているため、知る必要はありません。

まず、 aDestroyは悪い考えのように聞こえます。さらに、他のオブジェクトの破壊に責任を負わないオブジェクトで呼び出された場合。Destroyメソッドからのコードは、ApplicationServiceC# とは対照的に、完全に決定された時点で呼び出される .

これを完了すると、 を所有していないMyClassため、 を破棄するのは の責任ではないことに (うまくいけば) 気付くでしょうappService_。のコードをデストラクタに移動すると、実際のサービスを実際に管理し、実際にサービスを自動的に破棄するのは、 ConsumerClass(またはむしろメソッド)の責任です。RAII がクリーンで自動的な方法ですべてを実現する方法は素晴らしいと思いませんか?DoSomethingDestroy

class MyClass
{ 
public:
   MyClass(ApplicationService& app): appService_(app)
   {
   }

private:   
   ApplicationService& appService_;
}

class ConsumerClass
{
    DoSomething()
    {
        CustomApplicationService customAppService;
        MyClass myclass(customAppService);
        myclass...
    }
}

class ApplicationService
{
public:
    virtual ~ApplicationService()
    {
        //code from former Destroy method
    }
}

class CustomApplicationService
{
public:
    virtual ~CustomApplicationService()
    {
        //code from former Destroy method
    }
}

これは私見であり、完全にクリーンな C++ の方法であり、この問題は間違いなくスパムshared_ptrの理由ではありません。専用のメソッドが本当に必要でDestroy、コードをデストラクタに移動できない場合でも (これは、設計を考え直す動機となります)、再び as を呼び出すことになりますが、DestroyMyClassは appService_ を破棄する責任を負いませんDoSomething

編集:あなたの更新(および私のばかげたsomething議論の見落とし)によると、あなたのデザインは確かにかなり正しいようです(少なくとも変更を台無しにできない場合ApplicationService)、申し訳ありません。

クラスメンバーは構築の逆順で破棄されるはずですが、これがローカル自動変数にも当てはまるかどうかはわかりません。デストラクタが定義された順序で呼び出されるようにするためにできることは、単純なブロックを使用してネストされたスコープを導入することです。

void DoSomething()
{
    CustomApplicationService customAppService;
    {
        MyClass myclass(customAppService);
        myclass...
    }       // myclass destroyed
}       // customAppService destroyed

もちろん、shared_ptrs はさておき、動的割り当てを使用する必要はまったくありません。customAppServiceネストされたブロックはコードを少し吹き飛ばしますが、非動的な方法で理由もなく適用された動的割り当ての醜さには何も反しません. ;)

于 2011-11-01T23:14:26.907 に答える
2

C ++では、一般に、オブジェクトは、作成された順序とは正反対の順序で破棄されます。

あなたの例に基づいて、MyClass前に破壊されますCustomApplicationService

例外は、デストラクタが明示的に呼び出された場合です。ただし、この段階では、この例外について心配する必要はないと思います。

もう1つの微妙な点は、静的初期化順序の大失敗と呼ばれます。ただし、これは自動(スタック)変数には適用されません。

編集:
C++2003から-「逆順」を探しました

6.6.0.2

On exit from a scope (however accomplished), destructors (12.4) are called for all 
constructed objects with automatic storage duration (3.7.2) (named objects or 
temporaries) that are declared in that scope, in the reverse order of their
declaration. ... [Note: However, the program can be terminated (by calling exit()
or abort()(18.3), for example) without destroying class objects with automatic
storage duration. ]
于 2011-11-01T22:54:14.907 に答える