2

最近、次のメモリ バグが発生しました。ここでは簡単に見つけることができますが、より複雑なコードでは検出が難しくなる可能性があります。

class Foo : public IFoo {
  const Bar& bar_;
public:
  Foo(const Bar& bar) : bar_(bar) {
  }
  void test() {
    // access bar_ here
  }
};

int baz() {
  IFoo* foo = NULL;
  if(whatever) {
    Bar bar;
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // segmentation fault
}

バグは、Barすぐに範囲外になり、破棄されてから で使用されることfoo->test()です。Bar1 つの解決策は、を使用してヒープ上に作成することBar* bar = new Bar()です。ただし、特定のコードブロックに固有のものであっても、最後にBar* barアクセスできるようにポインターをトップレベルに保持する必要があるため、これを行うのは好きではありません。deleteBarif(whatever){}

別の解決策は ですがboost::shared_ptr<Bar>、これだけを書くことはできません:

  if(whatever) {
    boost::shared_ptr<Bar> bar(new Bar());
    foo = new Foo(*bar);
  }

もすぐに範囲外になりshared_ptr、含まれているオブジェクトが破棄されるためです。

要するに、この問題を取り除くには、どこでも、メンバー変数として、のコンストラクターなどを使用shared_ptrする必要があります。これらの問題を一般的に解消するには、すべての API などで を使用する必要があります。醜い。しかし、それは正しいことですか?これまでのところ、参照カウント オブジェクトを作成するために時々使用してきましたが、API をクリーンな状態に保っています。一度使用すると、どこでも使用しなければならないというこの問題にどのように対処しますか?FooFooshared_ptrshared_ptrshared_ptr

(また、これらの参照カウント ポインターを使用する場合は、本当に必要shared_ptrかどうかweak_ptrなどについて心配し始める必要があります。)

そして、私は何を同等のものとして使用しFoo(const Bar& bar)ますか? Foo(const shared_ptr<const Bar> bar)?

もちろん、別のオプションとして、Barおよび他のオブジェクト内に参照カウントを追加pimplし、独自のカウンターを使用することもできますが、一般的なルールとしては面倒です。

4

6 に答える 6

10

実際、私どこでも使用しshared_ptrています... 見栄えを良くする方法はいくつかあります。私が使用する 1 つの規則は、定義された各クラスの typedef です。

class AClass {
public:
    typedef boost::shared_ptr<AClass> Ptr;
    typedef boost::weak_ptr<AClass> Ref;
    //...
};

コードをより読みやすくします:)

特定の問題については、バーをポインターで渡すことができることを覚えておいてください。ただし、ヒープに割り当てることを忘れないでください。

于 2010-02-03T13:58:20.830 に答える
2

bar byref を Foo に渡さない (つまり、Foos コンストラクターを変更する) か、それをコピーして Foo に複製を保持する方がよいでしょう。もちろん、これが不可能な場合もあります。

コードを. shared_ptr<Bar>_shared_ptr<Bar>BarPtr

于 2010-02-03T13:55:51.997 に答える
1

私は実際に別の解決策を提案します...

混乱だけでなく、複数のオブジェクトが同じアイテムへのハンドルを持っていると推論するのが難しいため、参照カウントはあまり好きではありません。

class IBar
{
public:
  virtual ~IBar() {}
  virtual IBar* clone() const;
};

class Foo: public IFoo
{
public:
  Foo(const IBar& ibar): m_bar(ibar.clone()) {}
  Foo(const Foo& rhs): m_bar(rhs.m_bar->clone()) {}
  Foo& operator=(const Foo& rhs) { Foo tmp(rhs); this->swap(tmp); return *this; }
  virtual ~Foo() { delete m_bar; }

  void swap(Foo& rhs) { std::swap(m_bar, rhs.m_bar); }

private:
  IBar* m_bar;
};

コピーを作成したため、コードが機能するようになりました:)

もちろん、Bar がポリモーフィックでない場合は、はるかに簡単になります。

参照を使用していなくても、実際に参照が必要ですか?


あなたが間違っていることがたくさんあるので、今は軌道から外れましょう。

  • foo = new Foo(bar)きれいではありません。例外が発生した場合、または混乱が発生した場合にメモリが削除されることをどのように保証しますreturnif? メモリを管理するオブジェクト、またはC++0x または Boost からauto_ptrの新しいオブジェクトに移行する必要があります。unique_ptr
  • 参照カウントを使用する代わりに、所有権を取得しないのはなぜFooですか? Foo(std::auto_ptr<Bar> bar)もうまくいくでしょう。

必要のない参照カウントを避けます。何かを共有するたびに、追跡が困難なバグのソースが実際にコード内に導入されます (および潜在的な副作用)。そのためHaskell、機能的なファミリーが人気を集めています:)

于 2010-02-03T16:04:12.930 に答える
1

コードの簡略化されたバージョンを提供していることは理解していますが、残りの部分を見ないと、今言っていることが実現可能かどうかわかりません。

fooは最長寿命のオブジェクトであるため、その内部にインスタンスを作成し、(コード内で)Bar他の変数を参照するのはどうですか?bar

一方、 ifBarは if ブロックの内部に固有のものであり、test()まったく必要ありません。Fooこの場合、ポインターを内部に保持しておき、if ブロックの最後で削除しても問題ありません。それ以外の場合は、ブロックに固有のものではない可能性があり、設計を少し再考する必要があります。

于 2010-02-03T14:34:48.643 に答える
1

また、これらの参照カウント ポインターを使用する場合、本当に shared_ptr が必要なのか、weak_ptr などが必要なのかについて心配し始める必要があります。

設計に従い、何がweak_ptr(ヒント: 参照ではありません) 何のためにあるのか (ヒント: 「サイクルを壊す」ためのものではありません) を理解していれば、心配する必要はありません。

もしあなたが心配し始めたら、それはあなたがデザインを持ったことがないか、デザインを理解していないからですshared_ptr.

于 2011-10-20T03:27:56.663 に答える
0

すぐに削除されないようにするには、バー オブジェクトをより高いスコープに移動するだけです。

int baz() {
  Bar bar;
  IFoo* foo = NULL;
  if(whatever) {
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // no more segmentation fault
}
于 2010-02-03T14:25:12.163 に答える