web-dev-qa-db-ja.com

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

共有ポインターにラップされたオブジェクトを宣言する場合:

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番目の方法の方が読みやすく、パフォーマンスの限界を押し上げることは期待できません。危険なものがないことを確認したいだけです。

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

79
Steve H

関数に共有ポインタを渡したいです。それで私を助けてもらえますか?

確かに、私はあなたを助けることができます。 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つは移動します。完全転送関数テンプレートは別のオプションです。

考えられるすべてのシナリオを網羅していると思います。どうもありがとうございました。

154

生のポインターを関数パラメーターとして使用することは、人々が不必要に怖がっていると思います。関数がポインターを保存しないか、その寿命に影響しない場合、生のポインターも同様に機能し、最小公分母を表します。たとえば、unique_ptrを、値またはconst参照によってパラメーターとしてshared_ptrを受け取る関数に渡す方法を検討してください。

void DoSomething(myClass * p);

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

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

17
Mark Ransom

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

Shared_ptr <>へのポインターは避けます。これは、現在raw_pointersを処理しているため、目的を無効にします。

3

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

2
djechlin