2012-01-03 11 views
47

私は、別のデータオブジェクトから移入された新しいFoobarオブジェクトを返すstaticファクトリメソッドを作成しました。私は最近、オーナーシップのセマンティクスに夢中になりました。このファクトリメソッドがunique_ptrを返すようにして、適切なメッセージを伝えているかどうか疑問に思っています。所有権セマンティクスのような生ポインタのためのunique_ptrを返す悪い習慣はありますか?

class Foobar { 
public: 
    static unique_ptr<Foobar> factory(DataObject data); 
} 

私の意図は、クライアントコードにポインタがあることを伝えることです。スマートなポインタがなければ、私は単純にFoobar*を返します。しかし、このメモリを削除して潜在的なバグを避けるようにしたいので、unique_ptrは適切な解決策のようです。クライアントがポインタの存続期間を延長したい場合は、unique_ptrを取得した後に.release()を呼び出します。

Foobar* myFoo = Foobar::factory(data).release(); 

私の質問は二つの部分に来る:

  1. このアプローチは、正しい所有権の意味を伝えていますか?
  2. 生ポインタの代わりにunique_ptrを返すのは「悪い習慣」ですか?
+0

あなたは、彼らがポインタを所有し、クライアントに伝えるためにunqiue_ptrを返しているの?これはまさに私が期待していたものとは正反対です(彼らは明示的に一意のポインタの所有権を取る必要があるからです)。 – jknupp

+1

代わりにmove-semanticsを使用することもできます(C++ 11を使用できる場合)。これにより、工場で作成されたオブジェクトの寿命を延ばす方法を決定するのはユーザーの責任です。 – evnu

+1

@evnuそれはあなたのために自動的に行われるものですか? –

答えて

58

ファクトリメソッドからstd::unique_ptrを返すことはうまくいき、推奨される方法です。それが伝えるメッセージは(IMO)です:あなたは今このオブジェクトの唯一の所有者です。さらに、あなたの便宜のために、オブジェクトはそれ自身を破壊する方法を知っています。

これは、生ポインタ(クライアントがこのポインタをどのように処分するかを覚えておく必要がある場所)を返す方がはるかに良いと思います。

しかし、私はあなたの寿命を延ばすためにポインタをリリースするあなたのコメントを理解していません。一般的に私はポインタは常にRAII構造体の一種によって管理されるべきだと思うので、releaseをsmartpointerに呼び出す理由はほとんどありません。(私がreleaseと呼ぶ唯一の状況はポインタを別の管理データ構造に置くことです追加のクリーンアップを正当化するために何かをした後で、unique_ptrは別のDeleterを持っています)。

したがって、クライアントは(とすべきである)単純であれば、複数のコピーが必要な場合、彼らは、オブジェクト(またはshared_ptrを必要として(例えば戻さ一つから構成され、移動されている別のunique_ptr、など)unique_ptrどこかに格納することができますポインタの)。だから、クライアント側のコードはより次のようになりますに

std::unique_ptr<FooBar> myFoo = Foobar::factory(data); 
//or: 
std::shared_ptr<FooBar> myFoo = Foobar::factory(data); 

個人的に私はまた、(この場合はstd::unique_ptr<Foobar>で)返されたポインタ型のためtypedefを追加することになりますし、または使用デリータ(この場合はstdで:: default_deleter)あなたのファクトリオブジェクト。これは後でポインタの割り当てを変更することを後で決めると(したがって、ポインタの破棄のための別のメソッドが必要になります。これは第2のテンプレートパラメータstd::unique_ptrとして表示されます)。 だから私はこのようなものだろう:

class Foobar { 
public: 
    typedef std::default_deleter<Foobar>  deleter; 
    typedef std::unique_ptr<Foobar, deleter> unique_ptr; 

    static unique_ptr factory(DataObject data); 
} 

Foobar::unique_ptr myFoo = Foobar::factory(data); 
//or: 
std::shared_ptr<Foobar> myFoo = Foobar::factory(data); 
+3

私は 'release()'が誤解を招くか混乱させるという私の言及を意味するものではありません。この新しいコードは、スマートポインタ(COM以外)を使用しないかなり大きな既存のアプリケーション(C++の1〜2百万行)になります。レガシーコードが効果的に「必要」になる場合、生のポインタに到達するための合理的な方法がないと、私のチームの他の人が気になることが分かります。私はもちろん、適切な警告を出し、可能な限りunique_ptr/shared_ptrに固執することを推奨します。私はあなたのtypedefのアイデアが本当に好きです。これはJames McNellisとは別にあなたの答えを設定します。洞察に感謝します。 –

17

std::unique_ptrは、それが指すオブジェクトを一意に所有しています。それは、「私はこのオブジェクトを所有しており、誰もそれをしていません」と述べています。

これはまさにあなたが表現しようとしていることです。あなたは「この機能の呼び出し元:あなたは今このオブジェクトの唯一の所有者です。あなたが喜んで、その生涯はあなたの責任です。

+0

確かに、 'std :: shared_pointer <>'を返さないのはなぜですか?同じ効果が得られますが、ポインタを複数コピーすることができます。 –

+8

@AndréCaron:あなたがしたいことが所有者を呼び出し元に転送するだけの場合、なぜ 'std :: shared_ptr'を使用するのですか?呼び出し元が望むのであれば、呼び出し元がオブジェクトの所有権を 'std :: shared_ptr'に渡すようにしてください(呼び出し元が望むなら別の種類のスマートポインタ)。 –

+7

@AndréCaron:理由は基本的にパフォーマンスです。ファクトリは、クライアントが 'std :: shared_ptr'によって' std :: unique_ptr'によって追加された機能性を持つかどうかを知ることができません。 'shared_ptr'は' unique_ptr'から構築され、割り当てられるので実際には何のメリットもありません – Grizzly

6

正確なセマンティクスを正確に伝えており、C++のすべての工場がうまくいくと思います。std::unique_ptr<T>は所有権の意味を一切持たず、非常に安価です。

関連する問題