25

私は C++ を学んでおり、2 つの単純な hello-world アプリケーションを作成しました。どちらも演算子のオーバーロードを使用していますが、ここに問題があります。最初の例では、オーバーロード演算子に 2 つの引数を指定できますが、問題ありません。

ヘッダ:

enum Element {a,b,c,d,e};
Element operator + (Element x, Element y);
//more overloads for -, *, / here

ソース:

Element operator + (Element x, Element y) {
    return ArrayOfElements[x][y];
}

しかし、私の 2 番目のアプリ (単純な複素数計算機) では、この方法は機能しませんでした。グーグルで理由を突き止めた後、私は次のコードにたどり着きました:

ヘッダ:

struct Complex {
        double Re;
        double Im;

        Complex (double R, double I) : Re(R), Im(I) { }

        Complex operator + (Complex &Number);
        //more overloads
    };

ソース:

Complex Complex::operator + (Complex &Number)
    {
        Complex tmp = Complex(0, 0);
        tmp.Re = Re + Number.Re;
        tmp.Im = Im + Number.Im;
        return tmp;
    }

現在は機能していますが、最初のコードではoperatorオーバーロードに 2 つの引数を入れることができたのに、2 番目のコードでは次のエラーが発生したのはなぜですか?

complex.cpp:5:51: error: 'Complex Complex::operator+(Complex, Complex)' must take either zero or one argument

クラスを使っても使わなくても同じです。私は多くのドキュメントを探してきましたが、2 番目の方法の方が正しいようです。たぶん、引数の型が異なるためでしょうか?

-Wall -pedanticを使用してパラメータでコンパイルされた両方のソースはg++、両方とも同じライブラリを使用しています。

4

5 に答える 5

42

次のようなクラスがあるとします。

class Element {
public:
    Element(int value) : value(value) {}
    int getValue() const { return value; }
private:
    int value;
};

などの二項演算子を定義するには、4 つの方法があります+

  1. publicクラスのメンバーのみにアクセスできる無料の関数として:

    // Left operand is 'a'; right is 'b'.
    Element operator+(const Element& a, const Element& b) {
        return Element(a.getValue() + b.getValue());
    }
    

    e1 + e2 == operator+(e1, e2)

  2. クラスのすべてのメンバーにアクセスできるメンバー関数として:

    class Element {
    public:
        // Left operand is 'this'; right is 'other'.
        Element operator+(const Element& other) const {
            return Element(value + other.value);
        }
        // ...
    };
    

    e1 + e2 == e1.operator+(e2)

  3. 関数としてfriend、クラスのすべてのメンバーにアクセスできます:

    class Element {
    public:
        // Left operand is 'a'; right is 'b'.
        friend Element operator+(const Element& a, const Element& b) {
            return a.value + b.value;
        }
        // ...
    };
    

    e1 + e2 == operator+(e1, e2)

  4. friendクラス本体の外側で定義された関数として- 動作は #3 と同じです。

    class Element {
    public:
        friend Element operator+(const Element&, const Element&);
        // ...
    };
    
    Element operator+(const Element& a, const Element& b) {
        return a.value + b.value;
    }
    

    e1 + e2 == operator+(e1, e2)

于 2013-03-15T20:48:35.173 に答える
7

operator+両方のオペランドを明示的な引数として取りたい場合は、フリー (つまり、非メンバー) 関数として定義する必要があります。

class Complex {
    friend Complex operator+(const Complex& lhs, const Complex& rhs);
}

Complex operator+(const Complex& lhs, const Complex& rhs) {
    ...
}

左のオペランドがプリミティブ型、または制御できないクラス (したがってメンバー関数を追加できない) の場合は、この形式を使用する必要があります

于 2013-03-15T20:36:39.703 に答える
6

+は二項演算子であるため、構造体/クラス内でオーバーロードすると、もう1つのオペランドしか指定できません。その理由は、最初のオペランドが暗黙的に呼び出し元オブジェクトであるためです。そのため、最初のケースでは、クラス/構造体のスコープ外であるため2つのパラメーターがありますが、2番目のケースでは、メンバー関数としてオーバーロードされています。

于 2013-03-15T20:40:17.420 に答える
1

オーバーロードされた関数がクラスのメンバー関数である場合、1 つの引数のみを渡し、「+」などのバイナリ演算を実行するために必要な他のオブジェクトを指す1 つの隠しパラメーター ( thisポインター) があります。このポインターはオペランドの 1 つを指し、オーバーロードされた関数を呼び出します。一方、他のオペランドは引数として渡されます。例:

class ExampleClass
{
public:
   int x;
   //a this pointer will be passed to this function
   ExampleClass& operator+(const ExampleClass& o){ return x+o.x; }
};



ExampleClass obj1, obj2, obj;
obj = obj1 + obj2; //the overloaded function is called as obj1.operator+(obj2) internally
                  //this pointer is passed to the function

オーバーロードされた関数がメンバー関数 (フリー関数またはフレンド関数) でない場合、オーバーロードされた関数に提供されるthisポインターはありません。この場合、コンパイラは、オペランドとして使用される関数への 2 つの引数を予期します。

class ExampleClass
{
    public:
       int x;
       //this pointer will not be passed to this function
       friend ExampleClass& operator+(const ExampleClass& o1, const ExampleClass& o2){ return o1.x+o2.x; }
};



obj = obj1 + obj2; //the overloaded function is called as operator+(obj1, obj2) internally
于 2016-06-09T14:55:35.020 に答える