简单的方法是使用一些一厢情愿的想法:在编写加法操作之前,请认为您确实拥有这两个对象。
实际上,当您定义操作时,您正在编写一个稍后将应用的算法。在呼叫地点,当某些用户键入和时a+b
,必须是您要添加的类型的有效实例。在定义操作时,函数或方法的参数代表稍后将存在的有效实例的参数。a
b
由于这是家庭作业,我将采用不同的示例。考虑编写一个vector2d
包含两个表示x
和y
坐标的双精度值的类。
class vector2d {
public:
//...
private:
double x,y;
};
并且您想提供一些可以应用于vector2d
对象的操作。您可以定义operator+=
为将相同向量与第二个向量相加的结果存储在向量中的一种方式:
class vector2d {
public:
//...
vector2d& operator+=( const vector2d& rhs )
{
x += rhs.x;
y += rhs.y;
return *this;
}
};
为了约定,我们返回一个对vector2d
from的引用operator+=
。我们对我们调用操作的对象(这是一个成员函数,*this
对象)和我们作为参数的第二个对象进行操作。请注意,此时程序中没有创建单个对象,我们只是定义操作,而不是操作数。右边的参数rhs
是通过一个常量引用来处理的(我们得到一个我们承诺不会改变的对象的引用)。实际操作很简单,分别添加每个坐标,因为约定是返回对修改对象的引用,所以我们返回*this
.
我们已经定义了如何根据两个对象执行操作,现在我们可以使用它了:
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;
}
现在,为了能够进行调用,用户需要有两个对象。为了定义操作,我们实际上不需要对象的实例,我们通过参数对其进行抽象。但要实际使用操作,我们确实需要操作对象。此时它们实际上已创建并命名为a
and b
。然后用户调用操作:a += b
。因为 call 是一个成员方法,编译器会将它翻译成一些不太漂亮的: a.operator+=(b)
,也就是说,它会调用作为参数传递operator+=
的对象的成员方法。a
b
为了提供正确的加法运算,我们可以编写一个自由函数。添加不适用于两个参数中的任何一个,而是创建第三个参数,因此它不是成员方法是有道理的。
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
在函数中使用。第二个参数是通过引用传递的,我们保证不会改变它(因此是 const)。运算结果存储在c
.