web-dev-qa-db-ja.com

unique_ptrを関数に渡す

既存のコードを「近代化」しようとしています。

  • 現在、メンバー変数「Device * device_」を持つクラスがあります。
  • Newを使用して初期化コードにインスタンスを作成し、破壊に「delete device_」があります。
  • このクラスのメンバー関数は、many Device *をパラメーターとして取る他の関数を呼び出します。

これはうまく機能しますが、コードを「近代化」するには、変数を"std::unique_ptr<Device> device_"として定義するように変更し、deleteの明示的な呼び出しを削除する必要があると考えました。

私の質問はこれです-

  • 次に、device _変数をパラメーターとしてそれを必要とするすべての関数に渡すにはどうすればよいですか?

.getを呼び出して、各関数呼び出しで生のポインターを取得できます。しかし、それは見苦しく、そもそもunique_ptrを使用する理由のいくつかを無駄にします。

または、「every」関数を変更して、「Device *」タイプのパラメーターを受け取る代わりに、「std :: unique_ptr&」タイプのパラメーターを受け取るようにすることもできます。これは(私にとって)関数プロトタイプを多少難読化し、読みにくくします。

これのベストプラクティスは何ですか?他のオプションを見逃していませんか?

46
jcoder

Modern C++スタイルでは、2つの重要な概念があります。

  • 所有権
  • 無効

所有権は、オブジェクト/リソース(この場合、Deviceのインスタンス)の所有者に関するものです。さまざまな std::unique_ptrboost::scoped_ptrまたはstd::shared_ptrは所有権に関するものです。

Nullityははるかに単純ですが、特定のオブジェクトがnullであるかどうかを表すだけであり、他のことは一切気にせず、所有権については確かに気にしません!


あなたはrightクラスの実装をunique_ptr(一般的に)。ただし、目標がPIMPLの実装である場合は、ディープコピーセマンティクスを備えたスマートポインターが必要になる場合があります。

これは、クラスがこのメモリの唯一の原因であることを明確に示しており、それ以外の場合にメモリがリークする可能性のあるすべての方法をきちんと処理します。


一方、ほとんどのリソースのsersは、所有権についてあまり気にすることができませんでした。

関数がオブジェクトへの参照を保持しない限り(マップなどに保存します)、重要なのは、オブジェクトの有効期間が関数呼び出しの期間を超えることです。

したがって、パラメーターの受け渡し方法の選択は、可能性のあるNullityに依存します。

  • ヌルになることはありませんか? 参照を渡す
  • おそらくヌル? pointer、単純なベアポインターまたはポインターのようなクラス(たとえば、nullのトラップ付き)を渡します。

44
Matthieu M.

それは本当に依存しています。関数がunique_ptrの所有権を取得する必要がある場合、その署名は_unique_ptr<Device>_ bv valueを取得し、呼び出し元は_std::move_ポインターを取得する必要があります。所有権が問題にならない場合は、生のポインターシグネチャを保持し、get()を使用してポインターunique_ptrを渡します。これは見苦しくありませんif問題の関数は所有権を引き継ぎません。

14
juanchopanza

私は使うだろう std::unique_ptr const&。非const参照を使用すると、呼び出された関数にポインターをリセットする可能性が与えられます。
これは、呼び出された関数がポインターを使用できるが、それ以外は使用できないことを表現する良い方法だと思います。
だから私にとっては、これによりインターフェースが読みやすくなります。私は、渡されたポインターをいじる必要がないことを知っています。

7
mkaes

ベストプラクティスは、おそらくこの場合std::unique_ptrを使用しないことですが、それは依存します。 (通常、クラス内の動的に割り当てられたオブジェクトへの生のポインタを複数持つべきではありません。これも依存します。)この場合にしたくないことの1つは、std::unique_ptr(およびお気づきのとおり、std::unique_ptr<> const&は少し扱いに​​くく、難解です)。これがオブジェクト内で動的に割り当てられた唯一のポインタである場合、生のポインタとデストラクタ内のdeleteのみを使用します。そのようなポインターが複数ある場合は、それらをそれぞれ個別の基本クラスに委任することを検討します(未加工のポインターでもかまいません)。

2
James Kanze

それは実行不可能かもしれませんが、_Device*_のすべての出現を_const unique_ptr<Device>&_で置き換えることは良い出発点です。

明らかに_unique_ptr_ sをコピーすることはできず、移動したくないでしょう。 _unique_ptr_への参照に置き換えると、既存の関数の本体の本体が機能し続けることができます。

トラップがあります。呼び出し先がunique_ptr.reset()またはunique_ptr().release()を実行しないようにするには、_const &_を渡す必要があります。これはまだ変更可能なポインターをデバイスに渡すことに注意してください。このソリューションでは、_const Device_へのポインターまたは参照を渡す簡単な方法はありません。

1
J.N.