web-dev-qa-db-ja.com

参照または値でスマートポインター(shared_ptr)を返す方法

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

参照または値で返すことの可能な利点と欠点は何ですか?

2つの可能な手がかり:

  • 初期オブジェクトの破壊。(定数)参照によってshared_ptrを返す場合、参照カウンターはインクリメントされないため、別のコンテキスト(別のスレッドなど)。これは正しいです?環境がシングルスレッドの場合、この状況は同様に発生しますか?
  • コスト値渡しは確かに無料ではありません。可能な限りそれを避ける価値はありますか?

みんなありがとう。

81
Vincenzo Pii

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

既に述べたように、参照で返す場合、参照カウントを適切にインクリメントしないため、不適切なタイミングで何かを削除するリスクが生じます。それだけで、参照によって戻らない十分な理由になります。インターフェイスは堅牢でなければなりません。

現在、コストの懸念は 戻り値の最適化 (RVO)のおかげであまり意味がありません。そのため、最新のコンパイラでは、インクリメント/インクリメント/デクリメントシーケンスなどは発生しません。したがって、_shared_ptr_を返す最良の方法は、単に値で返すことです。

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

これは、最新のC++コンパイラにとっては明らかなRVOの機会です。すべての最適化がオフになっている場合でも、Visual C++コンパイラはRVOを実装しているという事実を知っています。また、C++ 11の移動セマンティクスでは、この懸念はさらに重要ではありません。 (ただし、確認する唯一の方法は、プロファイルと実験を行うことです。)

それでも納得していない場合、Dave Abrahamsには an article があり、値で返すための引数を作成します。ここでスニペットを再現します。記事全体を読むことを強くお勧めします。

正直に言うと、次のコードはどのように感じますか?

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

率直に言って、私はもっとよく知っているべきですが、それは私を緊張させます。原則として、get_names()が戻ったとき、vectorsのstringをコピーする必要があります。次に、namesを初期化するときに再度コピーする必要があり、最初のコピーを破棄する必要があります。ベクトルにN stringsがある場合、各コピーには文字列の内容がコピーされるのと同じ数のN + 1のメモリ割り当てとキャッシュにやさしいデータアクセスが必要になる可能性があります。

そのような不安に立ち向かうよりも、不必要なコピーを避けるために、参照渡しに頼る場合がよくあります。

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

残念ながら、このアプローチは理想とはほど遠いものです。

  • コードは150%増加しました
  • 名前を変更しているため、const- nessを削除する必要がありました。
  • 関数型プログラマが思い出させてくれるように、突然変異は参照の透明性と等式推論を損なうことで、コードの推論をより複雑にします。
  • 名前の厳密な値セマンティクスはなくなりました。

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

99
In silico

anyスマートポインター(shared_ptrだけでなく)については、参照を返すことは受け入れられるとは思わないため、参照または生のポインターで渡すのは非常にheします。どうして?後で参照によって浅くコピーされないことを確信できないためです。最初のポイントは、これが懸念される理由を定義します。これは、シングルスレッド環境でも発生する可能性があります。プログラムに不正なコピーのセマンティクスを入れるために、データに同時にアクセスする必要はありません。ポインターを渡すと、ユーザーがポインターで何をするかを実際に制御することはできません。そのため、APIユーザーに自分自身を掛けるのに十分なロープを与える誤用を奨励しないでください。

次に、可能であれば、スマートポインターの実装を確認します。建設と破壊はごくわずかに近いはずです。このオーバーヘッドが許容できない場合は、スマートポインターを使用しないでください!しかし、これを超えて、ポインターの使用を追跡するメカニズムへの相互排他的アクセスは、shared_ptrオブジェクトの単なる構築よりも遅くなるため、取得した並行性アーキテクチャーを調べる必要があります。

編集、3年後:C++のより新しい機能の出現により、呼び出し関数のスコープ外に決して存在しないラムダを単純に記述した場合に、より受け入れやすくなるように答えを微調整します。別の場所にコピーしました。ここで、共有ポインタをコピーするオーバーヘッドを最小限に抑えたい場合は、公平で安全です。どうして?参照が誤用されないことを保証できるからです。

19
San Jacinto