7

C++ の数値に似たクラスで算術演算子のオーバーロードを実装するための標準的または推奨されるパターンはありますか?

C++ FAQ から、ほとんどの問題を回避する例外セーフな代入演算子があります。

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

Number& Number::operator=(const Number &rhs)
{
   NumberImpl* tmp = new NumberImpl(*rhs.Impl);
   delete Impl;
   Impl = tmp;
   return *this;
}

しかし、他の演算子 (+、+= など) については、組み込み型の演算子のように振る舞うようにする以外に、ほとんどアドバイスはありません。

これらを定義する標準的な方法はありますか? これは私が思いついたものです - 私が見ていない落とし穴はありますか?

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}
4

4 に答える 4

4

Bjarne Stroustrup の著書 " The C++ Programming Language " の第 11 章 (演算子のオーバーロードに関する章) で、彼は複素数型 (セクション 11.3) のクラスについて説明しています。

そのセクションから私が気付いたことの 1 つは、彼が混合型操作を実装していることです...これはおそらく、どの数値クラスでも予想されることです。

一般的に、あなたが持っているものは良さそうです。

于 2009-01-07T23:51:53.217 に答える
4

演算子を記述する際に考慮すべき重要な点は、メンバー演算子が左のパラメーターで変換を受けないことです。

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}

これは、変換がthisfor メンバー関数に適用されず、これが演算子にまで及ぶためです。演算子が代わりexample operator + (example, example)にグローバル名前空間にある場合は、コンパイルされます (または pass-by-const-ref が使用された場合)。

その結果、対称演算子 like+および-は一般に非メンバーとして実装されますが、複合代入演算子 like+=および-=はメンバーとして実装されます (データも変更するため、メンバーである必要があります)。また、コードの重複を避けたいので、対称演算子は複合代入演算子の観点から実装できます(コード例のように、慣例では関数内で一時的に行うことをお勧めします)。

于 2009-01-08T01:06:29.667 に答える
3

慣例では、andをoperator+(const T&)andoperator-(const T&)の観点から書きます。プリミティブ型への加算およびプリミティブ型からの減算が理にかなっている場合は、プリミティブ型からオブジェクトを構築するコンストラクターを作成する必要があります。次に、オーバーロードされた演算子はプリミティブ型でも機能します。これは、コンパイラが適切なコンストラクターを暗黙的に呼び出すためです。operator+=(const T&)operator-=(const T&)

ご自身でおっしゃったように、必要のない機能にアクセス権を与えることは避けるべきです。しかし、上記のコードでは、operator+(Number, const Number&)個人的に両方のパラメーターを const 参照にし、temp を使用します。あなたの質問の下のコメンターがこれを見逃したのは当然のことだと思います。正当な理由がない限り、サプライズやトリックを避け、できるだけ明白にします。

コードを他の数値型 (たとえば ) と統合する場合はstd::complex、循環変換に注意してください。つまり、パラメーターを受け取るコンストラクターを提供するoperator OtherNumeric()場合にNumeric提供しないでください。OtherNumericNumeric

于 2009-01-07T23:52:32.700 に答える
3

演算子 X を演算子 =X の観点から記述するのが伝統的
です。また、標準演算子へのすべてのパラメーターが const であるのも伝統的です。

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}

代入演算子について言及しています。
しかし、あなたはコピーコンストラクターについて言及していませんでした。クラスには RAW ポインターの所有権があるため、これも定義する必要があります。代入演算子は、伝統的にコピー コンストラクターの観点から記述されます。

于 2009-01-08T00:28:59.367 に答える