114

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

参照または値で返すことの考えられる利点と欠点は何ですか?

2つの可能な手がかり:

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

みんなありがとう。

4

2 に答える 2

134

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

あなたが言ったように、それを参照によって返す場合、あなたは参照カウントを適切にインクリメントしません、それは不適切な時に何かを削除するリスクを開きます。それだけで、参照によって戻らないのに十分な理由になるはずです。インターフェイスは堅牢である必要があります。

戻り値の最適化(RVO)のおかげで、コストの懸念は今日では議論の余地があります。そのため、最近のコンパイラでは、インクリメント、インクリメント、デクリメントのシーケンスなどは発生しません。したがって、aを返す最良の方法shared_ptrは、単純に値で返すことです。

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

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

それでも確信が持てない場合は、DaveAbrahamsが値で返すことを主張する記事を掲載しています。ここでスニペットを複製します。記事全体を読むことを強くお勧めします。

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

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

率直に言って、もっとよく知っているはずなのに、緊張します。原則として、返品の際には、sのaをget_names() コピーする必要があります。次に、初期化するときにそれを再度コピーする 必要があり、最初のコピーを破棄する必要があります。ベクトルにNが存在する場合、各コピーにはN + 1のメモリ割り当てが必要になる可能性があり、文字列の内容がコピーされると、キャッシュに不利なデータアクセスが大量に発生する可能性があります。vectorstringnamesstring

そのような不安に立ち向かうのではなく、不必要なコピーを避けるために、私はしばしば参照渡しに頼ってきました。

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

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

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

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

于 2012-05-17T21:13:08.193 に答える
25

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

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

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

于 2012-05-17T21:17:42.997 に答える