共有ポインタを引数として渡す


88

共有ポインタでラップされたオブジェクトを宣言すると、次のようになります。

std::shared_ptr<myClass> myClassObject(new myClass());

それから私はそれをメソッドへの引数として渡したいと思いました:

DoSomething(myClassObject);

//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
   arg1->someField = 4;
}

上記は単にshared_ptの参照カウントをインクリメントするだけで、すべてがクールですか?それとも、ダングリングポインタを残しますか?

あなたはまだこれをすることになっていますか?:

DoSomething(myClassObject.Get());

void DoSomething(std::shared_ptr<myClass>* arg1)
{
   (*arg1)->someField = 4;
}

(スマートポインタ全体ではなく)1つのアドレスをコピーするだけでよいため、2番目の方法の方が効率的かもしれないと思いますが、1番目の方法の方が読みやすく、パフォーマンスの制限を押し上げることはないと思います。危険なものがないことを確認したいだけです。

ありがとうございました。


14
const std::shared_ptr<myClass>& arg1
キャプテンオブヴリアス2012年

3
2番目の方法は壊れています。最初の方法は、所有権を共有するために関数が実際に必要な場合は慣用的です。しかし、DoSomething本当に所有権を共有する必要がありますか?それだけではなく、参照を取る必要がありますようにそれは...見えます
ildjarn

8
@SteveH:必要ありませんが、実際には必要ないのに、関数が呼び出し元に特別なオブジェクト所有権セマンティクスを強制する必要があるのはなぜですか?あなたの関数は、として良くないことは何もしませんvoid DoSomething(myClass& arg1)
ildjarn 2012年

2
@SteveH:スマートポインターの全体的な目的は、通常のオブジェクト所有権の問題を処理することです。それらがない場合は、そもそもスマートポインターを使用しないでください。;-]shared_ptr<>具体的には、実際に所有権を共有するには、値を渡す必要あります。
ildjarn 2012年

4
無関係:一般に、コンストラクターよりstd::make_shared効率的であるだけでなく、安全でもありstd::shared_ptrます。
–R。マルティニョフェルナンデス2012

回答:


171

関数への共有ポインタを渡したい。それを手伝ってくれませんか。

確かに、私はあなたを助けることができます。C ++の所有権のセマンティクスについてある程度理解していると思います。本当?

ええ、私はその主題にかなり満足しています。

良い。

わかりました、私はshared_ptr議論をする2つの理由しか考えることができません:

  1. 関数はオブジェクトの所有権を共有したいと考えています。
  2. この関数は、特に機能するいくつかの操作を実行します shared_ptrsでます。

どちらに興味がありますか?

私は一般的な答えを探しているので、実際には両方に興味があります。ただし、ケース2の場合の意味については興味があります。

このような関数の例には、次のものがあります。 std::static_pointer_cast、カスタムコンパレータ、または述語が含まれます。たとえば、ベクトルからすべての一意のshared_ptrを検索する必要がある場合は、そのような述語が必要です。

ああ、関数が実際にスマートポインタ自体を操作する必要があるとき。

丁度。

その場合は参考にすればいいと思います。

はい。また、ポインタが変更されない場合は、const参照を渡します。所有権を共有する必要がないため、コピーする必要はありません。それは別のシナリオです。

はい、わかった。他のシナリオについて話しましょう。

あなたが所有権を共有するもの?OK。所有権をどのように共有しますかshared_ptrますか?

それをコピーすることによって。

次に、関数はのコピーを作成する必要がありますshared_ptr、正しいですか?

明らかに。だから私はそれをconstへの参照で渡し、ローカル変数にコピーしますか?

いいえ、それは悲観的です。参照によって渡された場合、関数は手動でコピーを作成する以外に選択肢はありません。値が渡された場合、コンパイラーはコピーと移動のどちらかを選択して自動的に実行します。したがって、値を渡します。

いい視点ね。「スピードが欲しい?価値を渡す」という記事をもっと頻繁に覚えておく必要があります。

shared_ptrたとえば、関数がをメンバー変数に格納するとどうなるでしょうか。それは冗長なコピーを作成しませんか?

この関数は、shared_ptr引数をストレージに移動するだけです。引っ越しshared_ptrと、参照カウントが変更されないため安価です。

ああ、いい考えだ。

しかし、私は3番目のシナリオを考えています。操作したくない場合shared_ptrや所有権を共有したくない場合はどうなりますか?

その場合、shared_ptrは機能とはまったく関係ありません。ポインティを操作する場合は、ポインティを取得し、呼び出し元に必要な所有権セマンティクスを選択させます。

そして、私はポインティを参照または値でとるべきですか?

通常のルールが適用されます。スマートポインタは何も変更しません。

コピーする場合は値で渡し、コピーを避けたい場合は参照で渡します。

正しい。

うーん。さらに別のシナリオを忘れたと思います。所有権を共有したいが、特定の条件にのみ依存する場合はどうなりますか?

ああ、興味深いエッジケース。私はそれが頻繁に起こるとは思わない。ただし、それが発生した場合は、値を渡して必要がない場合はコピーを無視するか、参照を渡して必要に応じてコピーを作成することができます。

私は最初のオプションで1つの冗長なコピーを危険にさらし、2番目のオプションで潜在的な動きを失います。ケーキを食べて食べられないの?

それが本当に重要な状況にある場合は、2つのオーバーロードを提供できます。1つはconst左辺値参照を取り、もう1つは右辺値参照を取ります。1つはコピーし、もう1つは移動します。完全転送関数テンプレートは別のオプションです。

私はそれがすべての可能なシナリオをカバーしていると思います。どうもありがとうございました。


2
@ジョン:何のために?これは、私がAを行うべきか、Bを行うべきかについてのすべてです。オブジェクトを値/参照で渡す方法は誰もが知っていると思います。
sbi 2012年

9
「それは、コピーを作成するためにconstへの参照でそれを渡すことを意味しますか?いいえ、それは悲観的です」のような散文は初心者を混乱させます。constへの参照を渡すと、コピーは作成されません。
ジョン2012年

1
@Martinho「先のとがった人を操作したいのなら、先のとがった人を連れて行く」というルールに同意しません。この回答で述べられいるように、オブジェクトの一時的な所有権を取得しないことは危険な場合があります。私の意見では、デフォルトでは、関数呼び出しの期間中、オブジェクトの有効性を保証するために、一時的な所有権のコピーを渡す必要があります。
radman 2013年

@radmanリンク先の回答にそのルールを適用するシナリオはないため、まったく関係ありません。shared_ptr<T> ptr;(つまりvoid f(T&)、で呼び出されたf(*ptr))からの参照によって渡され、オブジェクトが呼び出しよりも長生きしないSSCCEを私に書いてください。あるいは、あなたが価値を渡しvoid f(T)、トラブルが発生する場所を書いてください。
R. Martinho Fernandes

@Martinhoは、単純な自己完結型の正しい例について、私のサンプルコードを確認してください。この例は、void f(T&)スレッド用であり、スレッドを含みます。私の問題は、あなたの答えのトーンが、shared_ptrの所有権を渡すことを思いとどまらせることだと思います。これは、最も安全で、最も直感的で、最も複雑でない方法です。shared_ptr <>が所有するデータへの参照を抽出するように初心者にアドバイスすることは、非常に反対です。誤用の可能性は高く、メリットは最小限です。
radman 2013年

22

生のポインタを関数のパラメータとして使うことを不必要に恐れていると思います。関数がポインターを格納しない場合、またはその存続期間に影響を与えない場合、生のポインターも同様に機能し、最小公分母を表します。たとえば、値またはconst参照のいずれかによって、をパラメーターとしてunique_ptr受け取る関数にaを渡す方法を考えてみてshared_ptrください。

void DoSomething(myClass * p);

DoSomething(myClass_shared_ptr.get());
DoSomething(myClass_unique_ptr.get());

関数パラメーターとしての生のポインターは、それが本当に重要である呼び出しコードでスマートポインターを使用することを妨げません。


8
ポインタを使用している場合は、参照だけを使用しないのはなぜですか?DoSomething(*a_smart_ptr)
Xeo 2012年

5
@Xeo、その通りです-それはさらに良いでしょう。ただし、NULLポインタの可能性を考慮する必要がある場合もあります。
マークランサム2012年

1
比較:あなたは、出力パラメータとして生のポインタを使用しての規則を持っている場合には、例えば、変数が変更される可能性があることコード、呼び出すことで発見する方が簡単ですfun(&x)しをfun(x)。後者の例xでは、値で渡すか、constrefとして渡すことができます。上記のようnullptrに、出力に興味がない場合は合格することもできます...
アンドレアスマグナソン2012

2
@AndreasMagnusson:その出力パラメーターとしてのポインターの規則は、別のソースから渡されたポインターを処理するときに誤った安心感を与える可能性があります。その場合、呼び出しはfun(x)であり、* xが変更され、ソースには警告する&xがないためです。
Zan Lynx 2013年

関数呼び出しが呼び出し元のスコープよりも長生きした場合はどうなりますか?共有ptrのデストラクタが呼び出された可能性があり、関数は削除されたメモリにアクセスしようとします。その結果、UB
SridharThiagarajan19年

4

はい、shared_ptr <>に関する全体的な考え方は、複数のインスタンスが同じrawポインターを保持でき、shared_ptr <>の最後のインスタンスが破棄された場合にのみ基になるメモリが解放されるということです。

shared_ptr <>へのポインターは避けます。これは、raw_pointersを再度処理しているため、目的が損なわれるためです。


2

最初の例の値渡しは安全ですが、より良いイディオムがあります。可能な場合はconst参照を渡します-スマートポインターを扱う場合でも「はい」と言います。2番目の例は正確には壊れていませんが、非常に壊れてい!???ます。愚かな、何も達成せず、スマートポインターのポイントの一部を打ち負かし、物事を逆参照して変更しようとすると、バグのある苦痛の世界にあなたを置き去りにします。


3
あなたが生のポインタを意味するのであれば、誰もポインタを渡すことを提唱していません。所有権の競合がない場合に参照で渡すことは確かに慣用的です。重要なのは、shared_ptr<>具体的には、実際に所有権を共有するにはコピーを作成する必要があるということです。への参照またはポインタがあるshared_ptr<>場合、何も共有しておらず、通常の参照またはポインタと同じ存続期間の問題が発生します。
ildjarn 2012年

2
@ildjarn:この場合、定数参照を渡すことは問題ありません。shared_ptr呼び出し元のオリジナルは関数呼び出しよりも長持ちすることが保証されているため、関数はshared_ptr<> const &。を使用しても安全です。後でポインタを格納する必要がある場合は、コピーする必要があります(この時点で、参照を保持するのではなく、コピーを作成する必要があります)が、必要がない限り、コピーのコストを負担する必要はありません。それ。私は....この場合には一定の基準を渡すために行くだろう
デビッド・ロドリゲス- dribeas

3
@David: "この場合、定数参照を渡すことは問題ありません "正気のAPIではありません-APIが、通常のconst参照を取得するだけでなく、利用すらしていないスマートポインター型を要求するのはなぜですか?これは、const-correctnessの欠如とほぼ同じくらい悪いことです。技術的には何も害がないというのであれば、私は確かに同意しますが、それが正しいことだとは思いません。
ildjarn 2012年

2
...一方、関数がオブジェクトの存続期間を延長する必要がない場合shared_ptrは、インターフェイスからを削除しconstて、ポイントされたオブジェクトへのプレーン()参照を渡すことができます。
デビッドロドリゲス-ドリビアス2012年

3
@David:APIコンシューマーがshared_ptr<>最初から使用していない場合はどうなりますか?APIを使用するためだけに、呼び出し元がオブジェクトの有効期間のセマンティクスを無意味に変更する必要があるため、APIは実際には首の痛みにすぎません。「技術的に何も傷つけていない」と言う以外に、その中で主張する価値のあるものは何も見当たりません。「共有所有権が必要ない場合は、使用しないでください」について物議を醸すものは何もありませんshared_ptr<>
ildjarn 2012年

0

関数 DoSomething では、クラスのインスタンスのデータメンバーを変更しているmyClass ため、変更するのは(shared_ptr)オブジェクトではなく、管理対象(rawポインター)オブジェクトです。つまり、この関数の戻り点で、マネージ生ポインターへのすべての共有ポインターは、それらのデータメンバーを参照しmyClass::someFieldます。異なる値に変更されます。

この場合、オブジェクトを変更しないことが保証された関数にオブジェクトを渡します(所有オブジェクトではなくshared_ptrについて話します)。

これを表現するイディオムは次のとおりです。constref、like so

void DoSomething(const std::shared_ptr<myClass>& arg)

同様に、関数のユーザーに、生のポインターの所有者のリストに別の所有者を追加しないことを保証しています。ただし、生のポインタが指す基になるオブジェクトを変更する可能性を維持しました。

警告:つまり、shared_ptr::reset関数を呼び出す前に誰かが呼び出し、その時点でそれがraw_ptrを所有する最後のshared_ptrである場合、オブジェクトは破棄され、関数はダングリングポインターを操作してオブジェクトを破棄します。とても危ない!!!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.