1. ホーム
  2. c++

[解決済み] スマートポインタ(shared_ptr)を参照または値で返すには?

2022-10-14 08:50:44

質問

を返すメソッドを持つクラスがあるとします。 shared_ptr .

参照で返すか値で返すか、考えられる利点と欠点は何ですか?

考えられる手がかりは2つです。

  • 初期のオブジェクトの破壊。 を返すと shared_ptr を (const) 参照で返す場合、参照カウンタはインクリメントされないので、 別のコンテキスト (例えば別のスレッド) でスコープ外になったときにオブジェクトが削除される危険性があります。これは正しいのでしょうか?環境がシングルスレッドである場合、この状況は同様に起こり得るのでしょうか?
  • コストです。 パス・バイ・バリューは確かに無料ではありません。可能な限りそれを避ける価値があるでしょうか。

みなさん、ありがとうございます。

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

スマートポインタを値で返します。

おっしゃるとおり、参照で返すと参照カウントが適切にインクリメントされないので、不適切なタイミングで何かを削除してしまう危険性が出てきます。それだけでも、参照で返さない十分な理由になるはずです。インターフェイスは堅牢であるべきです。

のおかげで、コストの懸念は最近では無意味になっています。 戻り値の最適化 (RVO) のおかげで、最近のコンパイラではインクリメント-インクリメント-デクリメントのシーケンスなどが発生しないので、コストの心配は無用です。というわけで、最適な返し方としては shared_ptr を返すには、単純に値で返すことです。

shared_ptr<T> Foo()
{
    return shared_ptr<T>(/* acquire something */);
};

これは、最新のC++コンパイラにとって、死ぬほど明白なRVOの機会です。Visual C++ コンパイラーは、すべての最適化がオフの場合でも RVO を実装していることを私は知っています。また、C++11の移動セマンティクスでは、この懸念はさらに小さくなっています。(しかし、確かめる唯一の方法は、プロファイルして実験することです)。

まだ納得していないのであれば、Dave Abrahams が の記事 で、値で返すことについて論じています。ここではその一部を紹介しますが、記事全体を読むことを強くお勧めします。

正直なところ、次のコードを見てどう感じますか?

std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();

正直なところ、よく分かっているはずなのに、不安になるのです。原則的には get_names() が返されると、私たちは vectorstring s. そして、初期化するときに再びコピーする必要があります。 names を初期化するときに再びコピーし、最初のコピーを破棄する必要がある。もし、N個の string がある場合、各コピーは がある場合、文字列の内容がコピーされるたびに、N+1 個ものメモリ割り当てと、キャッシュに適さないデータ アクセスが必要になる可能性があります。

そのような不安に直面するよりもむしろ、私はしばしば参照渡しに戻って、不要なコピーを回避してきました。

get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );

残念ながら、この方法は理想とは程遠いものです。

  • コードは 150% 伸びました。
  • を落とさなければならなくなりました。 const -らしさは、名前を変異させるからです。
  • 関数型プログラマが私たちに思い出させるように、突然変異は参照透過性と等式推論を弱めることによって、コードを推論するのがより複雑になります。
  • 名前に対する厳密な値のセマンティクスを持たなくなりました。

しかし、効率を上げるためにこのようにコードを混乱させることが本当に必要なのでしょうか?幸いなことに、答えは「いいえ」であることがわかりました (特に C++0x を使用している場合はそうではありません)。