shared_ptr
を返すメソッドを持つクラスがあるとします。
参照または値で返すことの可能な利点と欠点は何ですか?
2つの可能な手がかり:
shared_ptr
を返す場合、参照カウンターはインクリメントされないため、別のコンテキスト(別のスレッドなど)。これは正しいです?環境がシングルスレッドの場合、この状況は同様に発生しますか?みんなありがとう。
値でスマートポインタを返します。
既に述べたように、参照で返す場合、参照カウントを適切にインクリメントしないため、不適切なタイミングで何かを削除するリスクが生じます。それだけで、参照によって戻らない十分な理由になります。インターフェイスは堅牢でなければなりません。
現在、コストの懸念は 戻り値の最適化 (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()
が戻ったとき、vector
sのstring
をコピーする必要があります。次に、names
を初期化するときに再度コピーする必要があり、最初のコピーを破棄する必要があります。ベクトルにNstring
sがある場合、各コピーには文字列の内容がコピーされるのと同じ数のN + 1のメモリ割り当てとキャッシュにやさしいデータアクセスが必要になる可能性があります。そのような不安に立ち向かうよりも、不必要なコピーを避けるために、参照渡しに頼る場合がよくあります。
_get_names(std::vector<std::string>& out_param ); ... std::vector<std::string> names; get_names( names );
_残念ながら、このアプローチは理想とはほど遠いものです。
- コードは150%増加しました
- 名前を変更しているため、
const
- nessを削除する必要がありました。- 関数型プログラマが思い出させてくれるように、突然変異は参照の透明性と等式推論を損なうことで、コードの推論をより複雑にします。
- 名前の厳密な値セマンティクスはなくなりました。
しかし、効率を上げるためにこのようにコードを台無しにすることは本当に必要ですか?幸いなことに、答えは「いいえ」であることがわかります(特にC++ 0xを使用している場合はそうではありません)。
anyスマートポインター(shared_ptrだけでなく)については、参照を返すことは受け入れられるとは思わないため、参照または生のポインターで渡すのは非常にheします。どうして?後で参照によって浅くコピーされないことを確信できないためです。最初のポイントは、これが懸念される理由を定義します。これは、シングルスレッド環境でも発生する可能性があります。プログラムに不正なコピーのセマンティクスを入れるために、データに同時にアクセスする必要はありません。ポインターを渡すと、ユーザーがポインターで何をするかを実際に制御することはできません。そのため、APIユーザーに自分自身を掛けるのに十分なロープを与える誤用を奨励しないでください。
次に、可能であれば、スマートポインターの実装を確認します。建設と破壊はごくわずかに近いはずです。このオーバーヘッドが許容できない場合は、スマートポインターを使用しないでください!しかし、これを超えて、ポインターの使用を追跡するメカニズムへの相互排他的アクセスは、shared_ptrオブジェクトの単なる構築よりも遅くなるため、取得した並行性アーキテクチャーを調べる必要があります。
編集、3年後:C++のより新しい機能の出現により、呼び出し関数のスコープ外に決して存在しないラムダを単純に記述した場合に、より受け入れやすくなるように答えを微調整します。別の場所にコピーしました。ここで、共有ポインタをコピーするオーバーヘッドを最小限に抑えたい場合は、公平で安全です。どうして?参照が誤用されないことを保証できるからです。