std :: unique_ptrを渡す方法は?


83

私はC ++ 11を使用する最初の試みをしていunique_ptrます。1つのクラスが所有しているが、頻繁に渡される私のプロジェクト内のポリモーフィックなrawポインターを置き換えています。

以前は次のような機能がありました。

bool func(BaseClass* ptr, int other_arg) {
  bool val;
  // plain ordinary function that does something...
  return val;
}

しかし、私はすぐに私が切り替えることができないことに気づきました:

bool func(std::unique_ptr<BaseClass> ptr, int other_arg);

呼び出し元は関数へのポインターの所有権を処理する必要があるため、私はしたくありません。だから、私の問題に対する最善の解決策は何ですか?

私は次のように、参照としてポインタを渡すことを考えました:

bool func(const std::unique_ptr<BaseClass>& ptr, int other_arg);

しかし、そうすることには非常に不快感を覚えます。第一に、すでに入力さ_ptrれているものを参照として渡すのは本能的ではないように思われるため、参照の参照となるものです。第二に、関数のシグネチャがさらに大きくなるためです。第三に、生成されたコードでは、変数に到達するために2つの連続したポインターの間接化が必要になるためです。

回答:


97

関数でポインティを使用する場合は、ポインティへの参照を渡します。ある種のスマートポインターでのみ機能するように関数を結び付ける理由はありません。

bool func(BaseClass& base, int other_arg);

そして、コールサイトで使用operator*

func(*some_unique_ptr, 42);

または、base引数をnullにできる場合は、署名をそのままにして、get()メンバー関数を使用します。

bool func(BaseClass* base, int other_arg);
func(some_unique_ptr.get(), 42);

4
これは素晴らしいですが、あなたstd::unique_ptrstd::vector<std::unique_ptr>議論のためにを取り除くカムですか?
CiroSantilli郝海东冠状病六四事件法轮功2016

31

使用する利点はstd::unique_ptr<T>(呼び出すことdeletedelete[]明示的に呼び出す必要がないことを除けば)、ポインターがnullptr(ベース)オブジェクトの有効なインスタンスであるか、それを指していることを保証することです。私はあなたの質問に答える後、私はこれに戻ってくるだろうが、最初のメッセージがあるDOが動的に割り当てられたオブジェクトの寿命を管理するスマートポインタを使用します。

さて、あなたの問題は実際にあなたの古いコードでこれをどのように使うかです

私の提案は、所有権を譲渡または共有したくない場合は、常にオブジェクトへの参照を渡す必要があるということです。このように関数を宣言します(const必要に応じて、修飾子の有無にかかわらず):

bool func(BaseClass& ref, int other_arg) { ... }

次に、を持っている呼び出し元std::shared_ptr<BaseClass> ptrnullptrケースを処理するかbool func(...)、結果を計算するように要求します。

if (ptr) {
  result = func(*ptr, some_int);
} else {
  /* the object was, for some reason, either not created or destroyed */
}

つまり、呼び出し元は、参照が有効であり、関数本体の実行中も引き続き有効であることを約束する必要があります。


これが、生のポインターやスマートポインターへの参照を渡すべきではないと私が強く信じている理由です。

生のポインタは単なるメモリアドレスです。(少なくとも)4つの意味のいずれかを持つことができます:

  1. 目的のオブジェクトが配置されているメモリブロックのアドレス。(良い
  2. 確実にできるアドレス0x0は参照解除できず、「nothing」または「noobject」のセマンティクスを持っている可能性があります。(悪い
  3. プロセスのアドレス可能なスペースの外にあるメモリブロックのアドレス(逆参照すると、プログラムがクラッシュする可能性があります)。(醜い
  4. 逆参照できるが、期待するものが含まれていないメモリブロックのアドレス。ポインタが誤って変更され、(プロセス内の完全に他の変数の)別の書き込み可能なアドレスを指している可能性があります。このメモリ位置に書き込むと、実行中に多くの楽しみが生じることがあります。これは、そこに書き込むことが許可されている限り、OSが文句を言わないためです。(Zoinks!

スマートポインタを正しく使用すると、通常はコンパイル時に検出できず、通常は実行時にプログラムがクラッシュしたり予期しないことを行ったりした場合にのみ発生する、かなり恐ろしいケース3と4が軽減されます。

スマートポインタを引数として渡すことには2つの欠点があります。コピーを作成せずにポイントされたオブジェクトのconst-nessを変更することはできず(オーバーヘッドが追加され、では不可能です)、2番目の()の意味が残ります。shared_ptrunique_ptrnullptr

設計の観点から、2番目のケースを(悪い)としてマークしました。これは責任についてのより微妙な議論です。

関数nullptrがパラメータとしてaを受け取ったときの意味を想像してみてください。まず、それをどうするかを決定する必要があります。欠落しているオブジェクトの代わりに「魔法の」値を使用しますか?動作を完全に変更し、他の何かを計算します(オブジェクトを必要としません)?パニックになって例外をスローしますか?さらに、関数が生のポインターによって2つ、3つ、またはそれ以上の引数を取るとどうなりますか?それらのそれぞれをチェックし、それに応じてその動作を適応させる必要があります。これにより、実際の理由なしに、入力検証にまったく新しいレベルが追加されます。

発信者は、これらの決定を下すのに十分なコンテキスト情報を持っている必要があります。言い換えると、悪いことは、あなたが知っているほど怖くないということです。一方、関数は、ポイントされたメモリが意図したとおりに安全に動作するという呼び出し元の約束を受け入れる必要があります。(参照は依然としてメモリアドレスですが、概念的には有効性の約束を表しています。)


25

Martinhoに同意しますが、参照渡しの所有権のセマンティクスを指摘することが重要だと思います。正しい解決策は、ここで単純な参照渡しを使用することだと思います。

bool func(BaseClass& base, int other_arg);

C ++で一般的に受け入れられている参照渡しの意味は、関数の呼び出し元が関数に「ここで、このオブジェクトを借用し、使用し、変更することができます(constでない場合)が、関数本体の期間。」これは、unique_ptrオブジェクトが短期間だけ借りられているため、実際の所有権の譲渡は発生しないため、の所有権規則と矛盾することはありません(誰かに車を貸した場合、タイトルに署名しますか?彼に?)。

したがって、参照(または生のポインタ)をから引き出すのは悪いように見えるかもしれませんが(設計上、コーディング慣行など)unique_ptr、実際には、によって設定された所有権ルールに完全に準拠しているためではありません。unique_ptr。そしてもちろん、クリーンな構文、が所有するオブジェクトのみに制限がないなど、他にも優れた利点がありますunique_ptr


0

個人的には、ポインター/スマートポインターから参照を取得することは避けています。ポインタがの場合はどうなるのnullptrでしょうか?署名をこれに変更した場合:

bool func(BaseClass& base, int other_arg);

nullポインタの逆参照からコードを保護する必要がある場合があります。

if (the_unique_ptr)
  func(*the_unique_ptr, 10);

クラスがポインタの唯一の所有者である場合、Martinhoの代替案の2番目はより合理的であるように思われます。

func(the_unique_ptr.get(), 10);

または、を使用することもできますstd::shared_ptr。ただし、責任を負う単一のエンティティが1つある場合deletestd::shared_ptrオーバーヘッドは報われません。


ほとんどの場合、std::unique_ptrオーバーヘッドがゼロであることをご存知ですか?
lvella 2017年

1
はい、私はそれを知っています。だから私は彼にstd::shared_ptrオーバーヘッドについて話しました。
Guilherme Ferreira
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.