69

私の質問は単純です。クラスManがあり、manの名前を返すメンバー関数を定義したい場合、次の2つのバリアントのどちらを選択しますか?

初め:

string name();

2番:

void name(/* OUT */ string &name);

最初のバリアントは、不要なコピーを作成するため、一種の非効率的です(ローカル変数->戻り値->割り当ての左側の変数)。

2番目のバリアントはかなり効率的に見えますが、それは私が書くことを泣きます

string name;
john.name(name);

単純ではなく

string name(john.name());

では、どのバリアントを好むべきでしょうか。また、効率と利便性/読みやすさの間の適切なトレードオフは何ですか?

前もって感謝します。

4

7 に答える 7

53

これは良い質問であり、質問しているという事実は、コードに注意を払っていることを示しています。ただし、良いニュースは、この特定のケースでは、簡単な方法があるということです。

最初のクリーンな方法が正しい方法です。ほとんどの場合、コンパイラは不要なコピーを削除します (通常は意味がある場合)。

編集 (2016 年 6 月 25 日)

残念ながら、David Abaraham のサイトは数年前からオフラインになっており、その記事はどこかに消えてしまったようです (archive.org のコピーは入手できません)。アーカイブ目的でローカル コピーを PDF として自由にアップロードしました。ここで見つけることができます

于 2012-05-11T14:16:30.153 に答える
27

最初のバリアントを使用します。

string name();

コンパイラは、おそらく不要なコピーを最適化します。戻り値の最適化を参照してください。

C++11 では、ムーブ セマンティクスは、コンパイラが RVO を実行しない場合でも、フル コピーを実行しないことを意味します。移動セマンティクスを参照してください。

また、代替手段が

void name(std::string& s);

次に、関数に渡されたときに何が起こりs、どのような値を持つことができるかを非常に明確に指定する必要があります。おそらく、多くの有効性チェックを行うか、単に入力を完全に上書きします。

于 2012-05-11T14:17:03.447 に答える
9

クラスのフィールドのゲッターを作成したいので、次のようにする必要があります。inline const std::string& name() const { return this->name; }

名前は const 参照として返されるため、クラス外で変更されることはなく、名前を返すことによってコピーが作成されることもありません。

その後、名前を操作したい場合は、コピーを行う必要があります。

于 2012-05-11T14:23:31.560 に答える
5

私は最初に行きます。戻り値の最適化と C++11 はコピーのオーバーヘッドを取り除きます。

于 2012-05-11T14:17:39.457 に答える
2

move-semantics (C++11) があるので、これを使用できます。

string name();

C++03 でも、コンパイラがこれを最適化する可能性があるため、これはほぼ良好です (戻り値の最適化の検索)。

于 2012-05-11T14:17:30.067 に答える
2

最適化のルール 1:測定、最適化、測定。または、クヌースが言ったように、「時期尚早の最適化は諸悪の根源です」。

std::string単純に戻ることがソフトウェアのパフォーマンスに大きな影響を与えるという強い兆候がない限り、そのままにしてください。大きな影響を測定できる場合は、クリティカル パスを見つけて最適化ます。パフォーマンス上のメリットがほとんどまたはまったくない可能性が高いが、コードの可読性、保守性、および堅牢性に悪影響を与える、おかしなプロジェクト全体の「最適化」を行わないでください。

于 2012-05-11T14:19:00.140 に答える
1

最初のバリアントを使用する必要があると思います。これは単純な getter メソッドであり、そのような getter/setter アプローチがあらゆる場所で使用されているためです。

于 2012-05-11T14:17:07.830 に答える