2010-12-06 5 views
2

新しいコードでは、お互いを参照するいくつかの異なるクラスがあります。このような何か(これは私の実際の状況が、似たような例ではありません):C++でのメモリ所有権を管理する最も良い方法は?共有ポインタまたは他のメカニズム?

class BookManager 
    { 
    ... 
    }; 

class Book 
    { 
    public: 
     void setBookManager(BookManager *bookManager) {m_bookManager = bookManager;} 
    private: 
     BookManager *m_bookManager; 
    }; 

すべての本がブックマネージャを意味するが、問題は、多くの書籍は、独自の特定のBookManagerを持っているということですが、いくつかの本共通のBookManagerを共有することができます。

呼び出し元はBookManagerで何をすべきかを実際に指定していませんが、約90%の場合BookManagerはBookと一緒に破棄できます。このケースの約10%で、同じBookManagerが複数の書籍に再利用され、BookManagerをBookで削除してはなりません。

Book :: setBookManagerの呼び出し元がBookManagerをもう覚えておく必要がないため、BookManagerをブックとともに削除すると、その90%のケースで便利です。それはただ本自体と一緒に死にます。

私はこれを解決するための2つの解決策を見ています。

まず、共有ポインタを広範囲に使用することです。その後、呼び出し元がBookManagerにそれ以上興味をもたない場合は、共有ポインタは保持されません。それでも興味がある場合や、BookManagerを複数の書籍で共有したい場合は、共有ポインタを保持して複数の書籍に渡します。

class Book 
    { 
    public: 
     void setBookManager(BookManager *bookManager, book takeOwnership=true) 
     { 
     m_bookManager = bookManager; 
     m_hasOwnership = takeOwnership; 
     } 
     ~Book() 
     { 
     if (m_hasOwnership && m_bookManager) delete m_bookManager; 
     } 
    private: 
     BookManager *m_bookManager; 
     bool m_hasOwnership; 
    }; 

第二の溶液は、はるかに簡単ようで、私たちは通常のポインタの構文を使用することができます(BookManager *として:

第二の代替は、このように、本の所有権をどうするか、明示的にブックを伝えることですstd::shared_ptr<BookManager>と反対)が、共有ポインタアプローチよりも「クリーン」ではないようです。

class BookManager 
    { 
    public: 
     typedef std::shared_ptr<BookManager> Ptr; 
     ... 
    }; 

私たちはこれを書くことができます::

別の方法としては、このようなBookManagerの中のtypedefを持っているかもしれません

以上、通常のポインタの構文のように見える
BookManager::Ptr bookManager; 

元の共有ポインタ構文。

どちらのアプローチでも経験がありますか? 他の提案はありますか?

答えて

7

C++では、共通オブジェクトへの共通の調整されていないアクセスがある場合、最も一般的なアプローチは、shared_ptrから得られる何らかの種類の参照カウントです。

唯一の欠点は、C++(特にライブラリ)では普及していないということです。そのため、生のポインタにアクセスする必要があることがあります。そのような場合は、オブジェクトを生きた状態に保つように注意する必要があります。

shared_ptrを使用した場合は、不可能でない限り、どこでも使用してみることをお勧めします。そして、必要ならtypedefを使用してください。

+0

「不可能でない限りどこでも使用してみてください」 - これが、循環参照を追跡するのが難しくなる方法です。 –

4

あなたはこれをかなり考えているようです。私にshared_ptrの理想的な使用のように見えます。

あなたのためにいくつかの値を追加するには、効率的なテンプレートshared_ptrを作成してhereにしてください。

1

オブジェクトがどこに所有されているか正確に分かっていて、そのオブジェクトを削除する際に注意してください(!)、裸のポインタを使用する必要があります。固有の親ポインタを保持するクラスの「スタック」があります。この場合、参照カウントは常に1になり、最後の子から最も深い要素にアクセスできます。ただし、ここでははるかに複雑な設定をしているようですが、スマートポインタを使用してください。裸のポインタがよりきれいに見えるかもしれませんが、unique_ptrを推奨しますが、変換前のコードでの移動対コピーの割り当てと、スイッチの結果として起こる致命的なエラーメッセージがあります。

1

Lou Francoは既に答えを出しましたが、補足として、2番目の実装のアイデアは基本的にはauto_ptrの機能です(オーナーシップが成立したとき)。

より良い解決策は、BookクラスにいくつかのBookManagerCacheクラスに存在するBookManagerへのハンドル(配列インデックスやハッシュなど)を保持させることです。 CacheクラスはBookManagersのライフタイムを管理する責任を単独で負います。

2

C++での「所有権」の2種類があります

  • が決定論的所有権:いつでもあなたは、リソース
  • 共有所有権の責任者を指すことができます任意の時点でいくつかの所有者があるかもしれませんしかし、それらは正確に特定するのが難しいです。

shared_ptrという名前が示すように、2番目のケースです。まれに、特に移動のセマンティクス、したがってunique_ptrの導入では必要ありませんが、実際に必要とされるケースでは、それは非常に貴重です。

あなたのケースを見ると、私の質問は次のとおりです。実際に共有所有権が必要ですか?

共有所有権を回避する典型的な解決策の1つは、Factoryが作成されたすべてのオブジェクトの唯一の所有者となるFactoryを使用し、Factory自体が生存している限り生きていることを保証することです。

それはshared_ptrを使用するよりも「少ない安全」かもしれませんが、非常に興味深いの引数があります。

  • は、オブジェクトの寿命は、もう一度
  • 危険がないをデバッグする方がはるかに簡単決定的です参照のサイクルを作成する(とメモリリーク)の事故

によってあなたはメモリの制約(早くオブジェクトは、より良い、それが削除されます。その場合には)されていない限り、あなたはBookManagerFactoryアプローチを取ることを望むかもしれませんここに。

関連する問題