运算符重载 += 操作符

上几篇文章我们介绍了加号运算符的重载,实现了两个类之间相加得出我们想要的结果,本文将介绍+=操作符的重载,使两个类的对象可以使用+=运算符来进行运算。其中要注意的是返回值为引用(&)的重要性。

要实现上面的需求,我们只需重载 operatpr+= 操作符即可,下面就是一个简单的示例,可以实现 c1+=c2的需求。

#include <iostream>

using namespace std;

class Complex
{
public:
	Complex(float x, float y)
		:_x(x), _y(y) 
	{
		cout << "Complex contructor" << endl;
	}
	
	void display()
	{
		cout << "(x = " << _x << ", y = " << _y << ")" << endl;
	}

	void operator+=(Complex another)
	{
		this->_x += another._x;
		this->_y += another._y;
	}

private:
	float _x;
	float _y;
};

int main(int argc, char* argv[])
{
	Complex c1(10.0, 15.0);
	Complex c2(20.0, 25.0);
	c1.display();
	c2.display();

	c1 += c2;
	c1.display();

	return 0;
}

运算的结果如下:

(x = 10, y = 15) // c1
(x = 20, y = 25) // c2
(x = 30, y = 40) // c1 + c2

上面的代码实现了我们的简单+=需求,还有很多细节的地方没有考虑到,并且程序的运行效率也是有待优化的。首先我们先考虑以下情况。

C++关键字int类型创建的对象可以实现连等的操作,比如下面的例子;

int a = 10, b = 20, c = 30;
a += b += c;
cout << a << endl;
cout << b << endl;
cout << c << endl;

最终结果

60
50
30

该表达式是从右向左依次执行的,首先计算 b+=c,结果b=50,c没有变动。然后计算a+=b,a=60,b=50。

如果我们要让类实现这样的功能,使用上面我们写好的代码可以成功吗?我们不妨测试一下,将main函数中的代码修改为如下的样子:

int main(int argc, char* argv[])
{
	Complex c1(10, 0);
	Complex c2(20, 0);
	Complex c3(30, 0);

	c1 += c2 += c3;

	c1.display();
	c2.display();
	c3.display();

	return 0;
}

此时再编译程序,你会发现出现了错误:

2015-05-17_224406

二进制“+=”: 没有找到接受“void”类型的右操作数的运算符(或没有可接受的转换),这句话是什么意思呢?我们分析一下程序的运算过程,一样是从右向左,相当于

c1.operator+=(c2.operator+=(c3))

首先是c2+=c3,在函数内部,我们给分别修改了 this->_x 和 this->_y的值,但此时你会发现,处理函数并没有返回任何值,这让接下来的 c1 与谁相加呢?这种情况下我们是不是要返回一个 Complex 的对象,才能使 c1 正常的与其相加,再次相加的过程会重复进入 operator+= 的重载函数中。所以,重载函数应该修改为如下的样子。

// 修改返回值为 Complex 对象
Complex operator+=(Complex another)
{
	this->_x += another._x;
	this->_y += another._y;
	// 将 this 指针解引用后返回
	return *this;
}

如上修改后,代码成功的运行了,我们得到了想要的结果:

(x = 60, y = 0) // c1.operator(c2.operator(c3))
(x = 50, y = 0) // c2.operator(c3)
(x = 30, y = 0) // c3

解决这个问题,我们只修改了函数的返回值,并在函数内部返回了解引用后的*this对象。这样就实现了连等的操作。接下来,我们继续考虑这样的情况。

在基础数据类型中,是允许出现这样的表达式的:

int a = 10, b = 20, c = 30;
(a += b) += c;

cout << a << endl;
cout << b << endl;
cout << c << endl;

运算的过程是,a首先与b结合,得到结果a=30,b未改变。随后得出的结果再与c结合,a+=c,结果a=60,c未改变。同样的案例,我们再次应用到我们自己重载的函数中试一下。将程序修改为如下的所示:

Complex c1(10, 0);
Complex c2(20, 0);
Complex c3(30, 0);

(c1 += c2) += c3;

c1.display();
c2.display();
c3.display();

运算后得出的结果是:

(x = 30, y = 0) // c1
(x = 20, y = 0) // c2
(x = 30, y = 0) // c3

这与上面基础数据类型运算出来的结果不一样啊,应该是 60 20 30 啊,怎么会变成这样的数据呢?我们细心来分析一下上面的案例,首先c1与c2先结合,c1+=c2后我们返回了*this,但返回值大家请注意,返回值并不是*this的引用,而是一份临时的对象与c3做了相加操作,最后临时对象销毁,而c1自身并没有参与到运算当中。所以最终得出的结果就是 30 20 30。那么想解决这个问题也非常简单,只需将返回值修改为Complex的引用即可,这样在c1与c2结合后得出的对象才是真正的c1,再与c3结合得出最终结果。

// 修改返回值为 Complex 对象的引用
Complex& operator+=(Complex another)
{
	this->_x += another._x;
	this->_y += another._y;
	// 将 this 指针解引用后返回
	return *this;
}

运算结果:

(x = 60, y = 0) //(c1 += c2) += c3
(x = 20, y = 0) //c2
(x = 30, y = 0) //c3

至此,+=运算符的重载我们就实现完毕了,几乎所有基本数据类型能实现的需求我们都实现了一遍,像-= *= /=运算符都与其类似。只需要按部就班的一步一步测试下来即可实现。

发表评论