1

これは簡単な質問だと思いますが、複雑なレガシーコードを見つめていると、森の木々が見えなくなります。このアプリケーションは数日間実行され、終了すると失敗します(もちろん、短いジョブでは失敗しません!)SEGVの疑いがあります。

以下のいくつかの擬似コードを使用してケースを簡略化しました(うまくいけば、正しく理解できます)。

人間の言葉で言えば:私は、単純なクラスABCへのポインターのベクトルを含む多くのものを含むクラスXYZを持っています(それが単純であると仮定しましょう)。これらのポインタは、XYZのデストラクタで削除されます。デストラクタが行うのはそれだけです。

次に、デストラクタのない2つの単純な仮想メソッドを持つ単純な基本クラスTheBaseがあります。

最後に、TomとDick(TheBaseから派生)とHarry(TheBaseから派生ではない)の3つのクラスがあります。これら3つはすべて、XYZオブジェクトへのconst参照から構築されています。したがって、XYZオブジェクトへのconst参照があります。また、デストラクタもありません。

メインでは、boost :: shared_ptrは、Tom、Dick、およびHarryオブジェクトごとに1つずつ定義されています。次に、XYZオブジェクトが作成されます。次に、そのXYZオブジェクトは、Tom、Dick、およびHarryオブジェクトへのconst参照として渡されます。その後、たくさんのことが起こり、メインが終了します。

では、これらすべてが範囲外になるとどうなりますか?特にXYZオブジェクト?これは正しく処理されますか?何かが複数回削除されるようです。

// A simple class (let's assume it is!)
class ABC
{
  // unimportant stuff.
}

// class XYZ has an array of ABC objects. All the destructor does is delete     those objects.
class XZY 
{
  public:
    XYZ(vector<string> v1,
        vector<string> v2,
        vector<string> v3 );
  virtual ~XYZ(){
          for ( i = 0; i < n, i++ ){
              delete my_abcs[i];
          }
  } 
private:
    vector <ABC*> my_abcs
  // lots of other methods & members
}

// Simple base class with only 2 simple virtual methods
class TheBase
{   
  public:
        virtual void minor_func1();
        virtual void minor_func2();
}

// A class derived from base class. Constructs with a const reference to an XYZ class.
class Tom:TheBase
{
    public:
        Tom( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
  // lots of other methods & members
}
Tom::Tom(const XYZ & xyz):my_xyz(xyz){
  ...
}

// Another class derived from base class. Constructs with a const reference to an XYZ class.
class Dick:TheBase
{
    public:
        Dick( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
    // lots of other methods & members
}
Dick::Dick(const XYZ & xyz):my_xyz(xyz){
...
}

// A class NOT derived from base class but still constructs with a const reference to an XYZ class.
class Harry:TheBase
{
  public:
        Harry( const XYZ & xyz )

    private:
        const XYZ & my_xyz; 
    // lots of other methods & members
}
Harry::Harry(const XYZ & xyz):my_xyz(xyz){
...
}

main (...){
  ...

  boost::shared_ptr <Tom> a_tom;
  boost::shared_ptr <Dick> a_dick;
  boost::shared_ptr <Harry> a_harry;
  ...

  XYZ a_xyz( ... );

  a_tom.reset( new Tom( a_xyz) );
  a_dick.reset( new Dick( a_xyz) );
  a_harry.reset( new harry( a_xyz) );

  ...
}
4

2 に答える 2

3

aによって管理されているオブジェクトは、それらをshared_ptr最後にshared_ptr指しているオブジェクトが破棄されるときに破棄されます。ローカル変数は、スコープ外になると、作成されたのとは逆の順序で破棄されます。あなたの場合、shared_ptr静的な存続期間(または疑似静的な存続期間、つまり、メインを離れるまで破棄されない動的に割り当てられたオブジェクト)がない場合は、a_xyz 破棄され、次に、によって示される3つのオブジェクトが破棄されます。shared_ptr。 _ これらのオブジェクトがデストラクタでへの参照を使用しない 場合(およびメインよりも長持ちする場所にコピーされていない場合)、問題はありません。a_xyzshared_ptr

于 2013-03-26T17:32:11.143 に答える
1

私は、単純なクラスABCへのポインターのベクトルを含む多くのものを含むクラスXYZを持っています(それが単純であると仮定しましょう)。これらのポインタは、XYZのデストラクタで削除されます。デストラクタが行うのはそれだけです。

余談ですが、この問題への答えはです。これは、タイプ内の問題のポインターを所有しstd::vector<std::unique_ptr<ABC>>ているという事実をカプセル化し、std::vector手動でそれらを破棄する必要をなくします。また、偶発的なコピーもブロックします。重要なデストラクタを実装する場合は、コピーの構築とコピーの割り当てを実装またはブロックする必要があります(3のルール)。

を使用するstd::vector<std::unique_ptr<ABC>>と、移動のみが可能になるため、ムーブ代入とムーブ代入はブロック解除され、コピー代入とコピー代入はブロックされます。

std::unique_ptr<T>わずかなオーバーヘッドがあります。

.get()唯一のコストは、基になるにアクセスする必要がある場合の一連の呼び出しですT*。これは、基本的に実行時のコストがゼロです。

メインでは、boost :: shared_ptrは、Tom、Dick、およびHarryオブジェクトごとに1つずつ定義されています。次に、XYZオブジェクトが作成されます。次に、そのXYZオブジェクトは、Tom、Dick、およびHarryオブジェクトへのconst参照として渡されます。その後、たくさんのことが起こり、メインが終了します。

同じスコープ内のC++のオブジェクトは、宣言されたのとは逆の順序で破棄されます。したがって、トム、ディック、ハリーの寿命はXYZオブジェクトよりも長くなります。

最後に、TomとDick(TheBaseから派生)とHarry(TheBaseから派生ではない)の3つのクラスがあります。これら3つはすべて、XYZオブジェクトへのconst参照から構築されています。したがって、XYZオブジェクトへのconst参照があります。また、デストラクタもありません。

参照(このコンテキストでは)は、参照されるものの存続期間に影響を与えません。参照はスマートポインタではなく、安全ではなく、チェックされていないエイリアスです。一般に、何かへの参照を作成するときは、オブジェクトがオブジェクトへの参照よりも長く続くようにするのがあなたの仕事です。

XYZのいずれかがスコープ外になった場合TomDickまたはHarryそれらの参照先にアクセスしたXYZ場合は、未定義の動作を呼び出しています。そうでなければ、あなたはそうしていません。

コードが非常に壊れやすいため、そうでない場合でも、これに依存するのは悪い習慣になる可能性があります。

(明確にするために:私が「この文脈で」と言ったとき、私はそれを意味しました。参照の寿命が問題のオブジェクトの寿命を変える文脈があります:参照が一時的な(匿名の)オブジェクトから直接構築されるとき、その匿名オブジェクトの存続期間は参照の存続期間まで延長されます。ただし、この方法で間接的に構築された参照にはこのプロパティがないことに注意してください。したがって、匿名オブジェクトの存続期間は延長されますが、延長さA& a = A();AませんB& b = a;。動作しA& get_A() { return A(); }; A& a = get_A();ませんがA get_A() { return A(); }; A& a = get_A();、ライフタイムの延長になります(この最後のものでは確実ではありません)。

于 2013-03-26T17:59:44.420 に答える