単純なアプローチは、少し希望的観測を使用することです。加算演算を作成する前に、2つのオブジェクトがあると考えてください。
実際、何が起こっているのかというと、操作を定義するときに、後で適用されるアルゴリズムを作成しているということです。呼び出しの場所で、一部のユーザーがタイプする場合a+b
、a
およびb
は、追加するタイプの有効なインスタンスである必要があります。操作を定義する際、関数またはメソッドへの引数は、後で存在する有効なインスタンスへの引数を表します。
これは宿題なので、別の例に従います。と座標vector2d
を表す2つのdoubleを含むクラスを作成することを検討してください。x
y
class vector2d {
public:
//...
private:
double x,y;
};
そして、オブジェクトに適用できるいくつかの操作を提供したいとしvector2d
ます。operator+=
同じベクトルに2番目のベクトルを追加した結果をベクトルに格納する方法として次のように定義できます。
class vector2d {
public:
//...
vector2d& operator+=( const vector2d& rhs )
{
x += rhs.x;
y += rhs.y;
return *this;
}
};
vector2d
慣例として、 fromへの参照を返しoperator+=
ます。操作を呼び出すオブジェクト(これはメンバー関数、*this
オブジェクト)と、パラメーターとして受け取る2番目のオブジェクトの両方を操作します。この時点では、プログラムに作成された単一のオブジェクトはなく、オペランドではなく操作を定義しているだけであることに注意してください。右側の引数rhs
は、定数参照を介して処理されます(変更しないことを約束するオブジェクトへの参照を取得します)。実際の操作は単純で、各座標を個別に追加します。規則では変更されたオブジェクトへの参照が返されるため、を返し*this
ます。
2つのオブジェクトに関して操作の実行方法を定義しました。これで、次のように使用できます。
int main() {
vector2d a( 5, 10 ); // assume that there is a constructor that
// takes 2 doubles in the ellipsis above...
vector2d b( 2.5, 7.5 );
a += b;
}
ここで、電話をかけることができるようにするには、ユーザーは2つのオブジェクトを持っている必要があります。オブジェクトのインスタンスを実際に必要としない操作を定義するために、パラメーターを使用してそれを抽象化します。しかし、実際に操作を使用するには、操作するオブジェクトが必要です。この時点で、それらは実際に作成され、名前が付けられa
ますb
。次に、ユーザーは操作を呼び出しますa += b
。呼び出しがコンパイラによってあまり美しくないものに変換されるのはメンバーメソッドであるため、つまり、引数として渡されるオブジェクトa.operator+=(b)
のメンバーメソッドを呼び出します。operator+=
a
b
適切な加算演算を提供するために、free関数を書くことができます。この追加は2つの引数のいずれにも適用されませんが、3番目の引数を作成するため、メンバーメソッドではないことは理にかなっています。
vector2d operator+( vector2d lhs, const vector2d & rhs )
{
lhs += rhs;
return lhs;
}
この実装は慣用的です(一般的なパターン)。実装したら、前の観点からoperatorX=
実装できます。operatorX
ここで注意が必要です。最初の引数を値で取得します。このように、コンパイラはlhs
この関数内でコピーを作成します。これは加算の最初の引数ではなく、そのコピーです。次に、既存の操作を使用してそのローカルコピーを変更し、結果をユーザーに返します。戻りオブジェクトも値によるものです。
使用法は似ています:
int main() {
vector2d a( 5, 10 );
vector2d b( 2.5, 7.5 );
vector2d c = a + b;
}
呼び出しの場所では、無料の関数であるため、a+b
に変換されます。operator+( a, b )
次に、最初の引数が値によって渡されるため、関数の場合と同様にのコピーa
が作成されて使用されlhs
ます。2番目の引数は参照によって渡され、変更しないことを約束します(したがってconst)。操作の結果はに保存されc
ます。