1. ホーム
  2. c++

[解決済み] 引数として共有ポインタを渡す

2023-01-07 07:57:26

質問

共有ポインタでラップしたオブジェクトを宣言した場合。

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;
}

2番目の方法は、(スマートポインタ全体ではなく)1つのアドレスだけをコピーすればよいので、より効率的かもしれないと思いますが、1番目の方法はより読みやすく、パフォーマンスの限界を超えることは予想されません。私はただ、それについて何か危険なことがないことを確認したいだけです。

ありがとうございます。

どのように解決するのですか?

<ブロッククオート

関数に共有ポインタを渡したいのですが、どうすればよいですか。あなたはそれを私を助けることができますか?

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

ええ、この話題にはそれなりに慣れています。

よかった。

OK、2つの理由しか思い浮かばないが、そのうちの1つは shared_ptr の引数を取る必要があります。

  1. この関数はオブジェクトの所有権を共有することを望んでいます。
  2. この関数は、特に shared_ptr s.

どれに興味がありますか?

<ブロッククオート

一般的な答えを求めているので、実は両方に興味があるんです。しかし、ケース2の意味が気になりますね。

このような関数の例としては std::static_pointer_cast やカスタムコンパレータ、述語などがあります。例えば、ベクターからすべてのユニークなshared_ptrを見つける必要がある場合、そのような述語が必要です。

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

その通りです。

<ブロッククオート

その場合、参照渡しでいいと思います。

そうですね。そして、ポインタを変更しないのであれば、const参照で渡したいところです。所有権を共有する必要がないので、コピーする必要はありません。それがもうひとつのシナリオです。

OK、了解しました。もう1つのシナリオについて話しましょう。

所有権を共有するもの?OKです。どのように所有権を共有するかというと shared_ptr ?

<ブロッククオート

コピーすることで

次に、この関数は、コピーを作成するために shared_ptr になるのですが、正しいですか?

明らかに だからconstの参照で渡して、ローカル変数にコピーする?

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

良い指摘だ。覚えておかなければなりませんね"。 スピードが欲しい?価値でパス。 の記事をもっと頻繁に思い出す必要があります。

待てよ、もし関数が shared_ptr をメンバ変数に格納したらどうでしょう?冗長なコピーになりませんか?

この関数では、単に shared_ptr 引数をそのストレージに移動します。を移動することは shared_ptr を移動することは、参照カウントを変更しないので、安価です。

ああ、いい考えですね。

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

その場合 shared_ptr は関数に全く関係ありません。もしポインティを操作したいのであれば、ポインティを取り、呼び出し側がどのような所有権のセマンティクスを望むかを選択できるようにします。

そして、ポインティを参照で取るべきか、値で取るべきか?

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

コピーするなら値で渡し、コピーを避けたいなら参照で渡す。

そうですね。

<ブロッククオート

ふむ。まだ別のシナリオを忘れているようですね。所有権を共有したいが、ある条件によってのみ共有したい場合はどうすればよいでしょうか。

ああ、面白いエッジケースですね。これが頻繁に起こるとは思っていません。しかし、それが起こったとき、値で渡して、必要なければコピーを無視するか、参照で渡して、必要ならコピーを作成することができます。

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

もしそれが本当に重要な状況であれば、2つのオーバーロードを提供することができます。1つはconst lvalueの参照を取り、もう1つはrvalueの参照を取ります。1つはコピー、もう1つは移動です。パーフェクトフォワード関数テンプレートは別の選択肢です。

これで、考えられるシナリオはすべて網羅できたと思います。ありがとうございました。