98

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

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

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

4

5 に答える 5

178

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

確かに、私はあなたを助けることができます。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引数をストレージに移動するだけです。aの移動shared_ptrは、参照カウントを変更しないため、安価です。

ああ、いい考えだ。

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

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

そして、私はポインティを参照または値で取得する必要がありますか?

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

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

右。

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

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

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

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

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

于 2012-05-31T03:00:22.587 に答える
24

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

void DoSomething(myClass * p);

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

関数パラメーターとしてのrawポインターは、呼び出し元のコードでスマートポインターを使用することを妨げるものではなく、実際に重要です。

于 2012-05-31T03:39:22.390 に答える
5

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

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

于 2012-05-31T02:07:16.500 に答える
2

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

于 2012-05-31T02:07:24.447 に答える
0

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

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

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

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

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

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

于 2020-01-22T19:45:14.520 に答える