web-dev-qa-db-ja.com

ゲッター関数でconst参照またはコピーを返しますか?

ゲッター関数からコピー(1)または参照(2)を返すにはデフォルトとしての方が良いのは何ですか?

class foo {
public:
    std::string str () { // (1)
        return str_;
    }

    const std::string& str () { // (2)
        return str_;
    }

private:
    std::string str_;
};

2)はより高速である可能性がありますが、(N)RVOのためにそうである必要はありません。 1)ぶら下がっている参照に関してはより安全ですが、オブジェクトがおそらく寿命を迎えるか、参照が保存されません。

クラスを作成し、パフォーマンスと寿命の問題が重要かどうか(まだ)わからない場合のデフォルトは何ですか?

追加の質問:メンバーがプレーンストリングではなくベクターの場合、ゲームは変わりますか?

41
cairol

まあ、それは本当に、デフォルトでbehaviourが何を期待するかに依存します。

呼び出し元がstr_unbeknownst(what a Word!)に加えられた変更を確認することを期待していますか?次に、参照を渡す必要があります。 refcountedデータメンバーがあり、それを返すことができる場合は良いかもしれません。

呼び出し元がコピーを取得することを期待する場合は、1)を実行します。

22
Aryabhatta

私の経験則では、int、stringなどの単純な基本データ型のコピーを返すことです。コピーがコストがかかる可能性があるもう少し複雑な構造の場合(前述のベクトルのように)、const-referenceを返すことを好みます。

18
Naveen

この場合、コンパイラーは(N)RVOを実行できません。 (名前付き)戻り値の最適化は、コピーする必要を回避するために、コンパイラーが戻り値の代わりに関数の自動変数を作成する最適化です。

std::string f()
{
   std::string result;
   //...
   return result;
}

コンパイラーが上記のコードを見ると(他の戻りが存在する場合はresult変数も返すと想定して)、変数resultには可能な限りの運命がコピーされていることがわかります。一時的に戻って破壊された。その後、コンパイラはresult変数を完全に削除し、一時的なリターンを唯一の変数として使用できます。私は主張します:コンパイラーは一時的な戻り値を削除せず、ローカル関数変数を削除します。一時的なリターンは、コンパイラの呼び出し規約を満たすために必要です。

クラスのメンバーを返すときは、メンバーが存在している必要があり、呼び出し規約では、返されたオブジェクトが特定の場所(通常はスタックアドレス)にある必要があります。コンパイラは、返されたオブジェクトの場所にメソッド属性を作成することも、コピーを作成することもできません。

文字列が「コピーするのが安っぽい」ように思えないため、参照を返しています。これは、動的メモリ管理などを備えた複雑なデータ型です。

「呼び出し元にコピーを取得させたい場合は、値で返す必要があります」という引数は、コピーをまったく除外しないため、意味がありません。発信者は引き続き次の操作を実行して、とにかくコピーを取得できます

string s = obj.str();

後でデータメンバーを直接参照できるようにするには、呼び出し側で明示的に参照を作成する必要がありますが、なぜそうするのでしょうか。コピーするのに安価な十分なユーザー定義タイプが確実にあります

  • スマートポインター
  • イテレータ
  • すべての非クラスタイプ。

パブリックインターフェイスの一部としてオブジェクトの内部への参照を返すことは、完全に悪い設計ではない場合でも、コードのにおいになる可能性があります。

パブリックインターフェイスで内部オブジェクトへの参照を返す前に、デザイナーは一時停止する必要があります。これにより、クラスのユーザーがデザインの一部に結合されます。多くの場合、それは完全に不要であり、時にはさらなる設計作業が必要であることを示します。コメンターが指摘したように、時々それは必要です。

4
ergosys

戻り値として値型を使用する特別な理由がない場合は、常にconst参照を返します。 (書き込み可能な)コピーが必要な(または必要と予想される)場合、まだ利用可能でない場合は、返されたクラスにコピートラクターと代入演算子を追加します。使用法については、次のことを考えてください。

const MyClass & ref = container.GetAt( 1234 ); // need only reference
MyClass copy = container.GetAt( 1234 ); // get writable copy 

実はこれはとても簡単ですよね。

1
ur.

私がconst-referenceを返すことに関して私が持っている唯一の問題は、非基本型に対して通常行うことですが、呼び出し側が "const"性を削除してから値を変更するのを止めることが何もないということです。

個人的には、そのようなコードはバグであることをお勧めします。彼らがあなたが参照を返していることを知っていて、constをキャストし続けるなら、それは彼らの頭にあります。

0
ScaryAardvark
  1. 小さな基本タイプの場合-intやlongなどのプリミティブとそのラッパー、および 'Point'などの他の基本的なもの-コピーを返します

  2. string、またはその他の複合型の場合-参照を返します。

0
Will