9

私はインターネット全体で+演算子を適切に実装する方法を検索し、見つけたすべての結果は次の手順を実行します。

const MyClass MyClass::operator+(const MyClass &other) const
{
    MyClass result = *this;  // Make a copy of myself. Same as MyClass result(*this);
    result += other;         // Use += to add other to the copy.
    return result;           // All done!
}

この「プロセス」についていくつか質問があります。

  1. この方法で+演算子を実装するのは愚かなことではありません。最初の行で代入演算子(クラスをコピーします)を呼び出し、次に戻り値でコピーコンストラクター(クラスもコピーします)を呼び出します。値によって、それは最初のコピーを破壊し、新しいものを作成します..率直に言って実際には賢くありません...)

  2. a = b + cと書くと、b + c部分がクラスの新しいコピーを作成し、次に'a='部分がそのコピーを自分自身にコピーします。b + cが作成したコピーを削除するのは誰ですか?

  3. クラスを2回処理せずに、またメモリの問題なしに+演算子を実装するためのより良い方法はありますか?

前もって感謝します

4

8 に答える 8

6
  1. これは事実上、代入演算子ではなく、コピーコンストラクターです。結局、足し算のような操作は新しい値を作成するので、どこかに作成する必要があります。コンパイラは戻り値の最適化を自由に実行できるため、これは見た目よりも効率的です。つまり、次に使用される場所に直接値を作成できます。

  2. resultローカル変数として宣言されているため、関数呼び出しで削除されます。ただし、RVO(上記を参照)が使用されている場合を除きます。この場合、実際には関数ではなく呼び出し元で作成されます。

  3. あまり; この方法は、最初に見たものよりもはるかに効率的です。

于 2010-05-19T13:54:16.427 に答える
5

そのような状況では、私はおそらく次のようなことを考えます:

MyClass MyClass::operator+(MyClass other) { 
     other += *this;
     return other;
}

Dave Abrahamsは、これがどのように機能するのか、そしてこの種のコードが最初はそうではないように見えても、通常は非常に効率的である理由を説明する記事をしばらく前に書きました。

編集(MSaltersに感謝):はい、これはを保持する可換性を想定/依存しますMyClass。の場合a+b != b+a、元のコードが必要なものになります(ほとんど同じ理由が当てはまります)。

于 2010-05-19T13:59:48.237 に答える
3

これは、を実装する正しい方法のようoperator+です。いくつかのポイント:

  • MyClass result = *this代入演算子を使用しません。記述されているかのように、コピーコンストラクターを呼び出す必要がありますMyClass result(*this)
  • で使用された場合の戻り値は一時的なa = b + cものと呼ばれ、コンパイラはそれを削除する責任があります(これは、ステートメントの最後、つまりセミコロンで、他のすべてが完了した後に発生する可能性があります)。それについて心配する必要はありません。コンパイラは常に一時的なものをクリーンアップします。
  • これ以上の方法はありません。コピーが必要です。ただし、コンパイラは一時コピーを最適化することが許可されているため、作成できると思われる数にはなりません。ただし、C ++ 0xでは、 moveコンストラクターを使用して、一時的なコンテンツの所有権をエンティティにコピーするのではなく譲渡することで、パフォーマンスを向上させることができます。
于 2010-05-19T13:56:08.713 に答える
3

最初の行で代入演算子(クラスをコピーする)を呼び出します

いいえ、これは(コンストラクターによる)コピー初期化です。

次に、戻り値のコピーコンストラクター(クラスもコピーします)

コンパイラーは、NRVOを使用してこのコピーを削除できます(通常は削除します)。

a = b + cと書くと、b + c部分がクラスの新しいコピーを作成し、次に'a='部分がそのコピーを自分自身にコピーします。b+cが作成したコピーを削除するのは誰か

他の一時的な値と同様に、コンパイラ。;それらは完全な式の終わりで削除されます(この場合、それは行の終わり以降を意味します。

クラスを2回処理せずに、またメモリの問題なしに+演算子を実装するためのより良い方法はありますか?

あまり。それはそれほど非効率的ではありません。

于 2010-05-19T13:56:55.547 に答える
2

私は答えるために最善を尽くします:

ポイント(1):いいえ、代入演算子は呼び出されません。代わりに、コンストラクターを呼び出します。とにかくオブジェクトを作成する必要があるため(operator+コピーを返すため)、これによって余分な操作が発生することはありません。

ポイント(2):一時resultはスタックに作成されるため、メモリの問題は発生しません(関数が終了すると破棄されます)。returnで、一時が作成され、代入(またはコピーコンストラクター)を使用して、破棄された後でも結果をa(in )に割り当てることができます。この一時的なものは、コンパイラによって自動的に破棄されます。a=b+c;result

ポイント(3):上記は規格で規定されているものです。コンパイラの実装者は、効果が標準で規定されているものと同じである限り、実装を最適化できることを忘れないでください。コンパイラは実際には、ここで発生するコピーの多くを最適化すると思います。上記のイディオムの使用は読みやすく、実際には非効率的ではありません。

PS Ioperator+は、演算子の両側で暗黙の変換を活用するために、非メンバーとして実装することを好む場合があります(意味がある場合のみ)。

于 2010-05-19T14:01:52.743 に答える
1

メモリの問題はありません(代入演算子とコピーコンストラクタが適切に記述されている場合)。これらのオブジェクトのすべてのメモリがスタックに取り込まれ、コンパイラによって管理されるからです。さらに、コンパイラーはこれを最適化し、a2回コピーするのではなく、ファイナルですべての操作を直接実行します。

于 2010-05-19T13:54:36.670 に答える
1
  1. これは、C++でoperator+を実装する適切な方法です。あなたがとても恐れているコピーのほとんどは、コンパイラによって排除され、C++0xでの移動セマンティクスの対象になります。

  2. クラスは一時的なものであり、削除されます。一時的なものをaにバインドすると、一時的なものconst&の有効期間はconst参照の有効期間まで延長されます。

  3. freefunctionとしてそれを実装することはもう少し明白かもしれません。MyClass :: operator +の最初のパラメーターは暗黙のthisであり、コンパイラーは関数をoperator +(const MyClass&、const MyClass&)に書き換えます。

于 2010-05-19T13:55:09.013 に答える
1

私が覚えている限り、Stroustrupの「C ++プログラミング言語」では、内部表現が操作の影響を受ける場合にのみ演算子をメンバー関数として実装し、影響を受けない場合は外部関数として実装することを推奨しています。operator + =に基づいて実装されている場合、operator+は内部表現にアクセスする必要はありません。

だからあなたは持っているでしょう:

class MyClass
{
public:
  MyClass& operator+=(const MyClass &other)
  {
    // Implementation
    return *this;
  }
};

MyClass operator+(const MyClass &op1, const MyClass &op2)
{
    MyClass r = op1;
    return r += op2;
}
于 2010-05-19T14:48:47.420 に答える