5

最近、テンプレートを使用して複雑なクラスの基本的な機能主義者(+、-、共役...)をオーバーロードするという割り当てを行いました。適切な戻り型 (より高い型へのキャスト) を見つけるために少し汗をかく必要がありましたが、最終的には完全に機能するようになりました。これが私のクラスの様子です -

template <typename T> class complex_t
{ 
private:
    T real;
    T imaginary;

public:

complex_t(T X, T Y)
  {
    real=X;
    imaginary=Y;
  }
}

しかし、+=、-= などの演算子を実装していないため、満点は取れませんでした。これらの演算子を実装することが重要なのはなぜですか? それを行うことで、実際に特定のメリットが得られるかどうか? 誰か考えを共有できますか?

前もって感謝します、

4

7 に答える 7

9

オーバーロード演算子 - クラス「Foo」のユーザーが次のようなコードを記述できるようにします。

Foo f1 = ...;
Foo f2 = ...;
f2 = f2 - f1;

オーバーロード演算子 -= により、ユーザーは書き込み可能

Foo f1 = ...;
Foo f2 = ...;

f2 -= f1;

-= をオーバーロードしない限り、2 番目の例は機能しませんが、ユーザーはおそらく機能することを期待しています。

編集して効率のポイントを組み込みます (私の回答は支持を得ているので、すべての詳細を 1 つの場所に保つために、他の回答から繰り返されるポイントを要約すると思いました。larsmans と Benjamin の功績)

f2 -= f1f2 = f2 - f1多くの場合、次の 2 つの理由からより効率的です。

  • operator -thisコピーを変更して返す前に、 のコピーを取得する必要があります。
  • operator -また、結果を値で返す必要があり (スタック オブジェクトへの参照を返すことはできません)、2 番目のコピーが発生する可能性があります。

operator -=一方、thisその場で変更するため、コピーは作成されません。

于 2012-10-05T11:57:12.037 に答える
9

A と B の 2 つのオブジェクトがあり、 を使用せずに A を B ずつインクリメントしたい場合は、次のoperator+=ようにします。

A = A + B;

通常の実装では、これには 3 番目の (一時的な) オブジェクトの作成が含まれ、それが A にコピーされます。ただし、 を使用するとoperator+=、A をその場で変更できるため、通常は作業が少なくて済むため、より効率的になります。

おそらくもっと重要なのは、それが言語にとって慣用的であることです。C++ プログラマーは、これができれば次のことを期待しています。

A = A + B;

彼らはこれを行うこともできます:

A += B;
于 2012-10-05T11:58:40.397 に答える
6

+=友達はその場で作業するので、クラスの新しいインスタンスを返す必要はありません。複素数の場合、それはそれほど問題にならないかもしれませんが、より大きな構造ではコピーにコストがかかる可能性があります。

例として、数学的な意味で任意の長さをサポートするベクトルを実装しているとします。

class Vector
{
    std::vector<double> elements;

  public:
    Vector operator+(double x)
    {
        // must return a copy!
        Vector v(*this);
        for (size_t i=0; i < elements.size(); i++)
            v.elements[i] += x;
        return v;
    }

    Vector &operator+=(double x)
    {
        // in-place operation
        for (size_t i=0; i < elements.size(); i++)
            elements[i] += x;
        return *this;
    }
};
于 2012-10-05T11:56:32.503 に答える
5

まず、+演算子を使用したクラスを指定すると、+=も機能することが期待されます。ただし、これは自動的には行われないため、実装する必要があります。

第二に、他の人が以前に指摘したように、クラスの実装と合計演算の定義によっては、明白な方法で+演算子を単に再利用するよりも+=より効率的に実装できる場合があります(これはコンパイラーによって自動生成される方法ですが、そうではありません)。

于 2012-10-05T12:03:17.377 に答える
4

それが組み込み演算子の働き方だからです。二項演算子 があるときはいつでも、一度しか評価されないことを除いて、 と同等のopバリアントがあり op=ます。オーバーロードされた演算子を定義している場合は、組み込み演算子でその動作をパターン化する必要があります (組み込み演算子で動作を自然にパターン化できない場合は、オーバーロードしないでください)。提供するが提供しないことは、提供するが提供しないのと同じです。a op= b;a = a op b;a++=<<=

実際には、算術演算子を実装する通常の方法は、クラスで演算子のみを定義し、演算子を使用して演算子をop=定義するクラス テンプレートのインスタンス化から派生させることです。たとえば 、次のようになります。opop=

class MyType : public BinaryOperators<MyType>
{
public:
    MyType& operator+=( MyType const& other );
    //  ...
};

BinaryOperators次のようになります。

template <typename ValueType>
class BinaryOperators
{
    friend ValueType
    operator+( ValueType const& lhs, ValueType const& rhs )
    {
        ValueType results( lhs );
        results += rhs;
        return results;
    }
    //  ...
};

(この場合、friend宣言は、フリー関数をクラス内で完全に定義できるようにするためのトリックにすぎません。)

于 2012-10-05T12:35:51.930 に答える
3

+ 演算子は設計上、完全に新しいオブジェクトを割り当てて構築しますが、+= 演算子は既存のオブジェクトを変更できます。これにより、ユーザーが += 演算子を に置き換えた場合よりもはるかに効率的に += 演算子を実装できますval1 = val1 + val2

また、クラスが + をオーバーロードすることがわかっている場合は、+= もオーバーロードすることを期待します。

于 2012-10-05T11:59:57.533 に答える
1

他のプログラマーが += がオーバーライドされることを期待する可能性に加えて、継承とオーバーライド機能に必要になる可能性があると思います。非常に抽象的でおそらく間違った意味で、特殊な整数を含む整数のリストに対して操作を実行したいとします。

MyModifiedInteger extends Integer {
  private boolean bigNumberFlag = false;
  ...
  @Override
  public void +=(int i) {
    this = this + i;
    if (this > 100) {
      this.bigNumberFlag = true;
    }
  }
}
MyModifiedInteger myModifiedInt = new MyModifiedInteger();
List<Integer> integers = new ArrayList<Integer>();
integers.add(myModifiedInt);
for (Integer i : integers) {
  i+=5;
}

ここでの考え方 (Java 継承を正しく適用した場合) は、Integers で += 演算子を使用するだけでなく、+= 操作を異なる方法で処理する継承クラスを使用することです。

于 2012-10-05T12:36:25.560 に答える