Bootstrap

【C++入门】运算符重载详解

1、什么是运算符重载

不可重载运算符运算符含义
.成员访问运算符
“->*”和“.*”成员指针访问运算符
::域运算符
sizeof长度运算符
? :条件运算符
#预处理符号

(1)运算符:运算符分为算术运算符(+、-、*、/)、关系运算符(=、!=、==)、逻辑运算符(&&、!)等;
(2)运算符重载:给运算符赋予新的含义,可以根据运算符操作的对象进行恰当的操作;
(3)不是所有的运算符都支持运算符重载,有的运算符的含义在任何情况下都是确定的,所以不需要重载;

2、为什么需要运算符重载?

(1)运算符都是有默认的含义的,比如"+“符号,表示两个对象的相加,对于像int、long等C++原生的类型,”+“符号可以很好的处理,因为这是明确且不变的;但是”+“进行相加的对象时用户自定义的,那默认的”+“在处理时就会出现问题,因为编译器根本不知道用户会如何定义类,更不知道怎么对两个对象进行相加的操作,这时候就需要定义类的程序员去将”+“号进行重载,重新定义针对类的”+“运算符操作;
(2)程序中操作都是以函数为单位的,运算符能进行操作实质也是编译器帮我们把运算符和某个函数进行绑定,当我们使用”+"运算符时,编译器会自动帮我们调用默认的相加函数;
(3)编译器提供的运算符默认函数,只能进行能提前确定的一些操作,基本上就是对C++原生自带类的操作,当涉及用户自定义的类操作时,就会出现问题,这时候就需要定义类的程序员自己去重新定义类的运算符,也就是运算符重载;
(4)当程序执行时,会去匹配运算符绑定的函数,类似于函数重载,如果没有对运算符进行重载就调用默认的运算符函数,如果匹配上重载的运算符函数就调用重载的函数;

3、运算符重载的优劣

class sample
{
	int a;
	int b;
	int c;
};

sample d, e, f;

//使用运算符重载
f = d + e;

//不使用运算符重载
f.a = d.a + d.a;
f.b = d.b + d.b;
f.c = d.c + d.c;

(1)首先运算符重载不是必须的,不使用运算符重载一样可以完成功能,C语言就没有运算符重载不也一样工作;
(2)重载运算符可以使得代码更简洁;参考上面的代码,如果针对sample类重载"+“运算符就可以很简洁的执行两个sample类对象的加操作,如果不重载”+"运算符就必须显示的执行每个类成员的相加操作;
(3)运算符重载机制加大了类库作者的工作量,减少了调用类库写功能的人的书写量,因为运算符重载的代码是类库作者写的;

4、运算符重载示例

4.1、示例代码

#include <iostream>

using namespace std;

//表示坐标的类
class coordinate
{
public:
	int x;					// x轴坐标
	int y;					// y轴坐标

	//构造函数
	coordinate();
	coordinate(int x0, int y0);

	//打印坐标的值
	void print(void);
	
	// 重载"+"运算符
	coordinate operator+(const coordinate& other);
	
};

coordinate::coordinate()
{
	x = 0;
	y = 0;
};

coordinate::coordinate(int x0, int y0)
{
	x = x0;
	y = y0;
};

void coordinate::print(void)
{
	cout << "(" << this->x << ", " << this->y << ")" << endl;
}

coordinate coordinate::operator+(const coordinate& other)
{
	coordinate tmp;
	tmp.x = this->x + other.x;
	tmp.y = this->y + other.y;
	
	return tmp;
}

int main(void)
{
	coordinate a(1, 3);
	coordinate b(2, 6);
	coordinate c;
	
	//执行加操作,此时因为coordinate类重载加号运算符,会调用coordinator类重载的加函数
	c = a + b;			
	//c = a.operator+(b); //上面的c = a + b等同于:c = a.operator+(b);
	
	a.print();
	b.print();
	c.print();
	
	return 0;
}

(1)运算符重载就是在类中去重新实现运算符对应的函数;
(2)运算符重载的函数格式:返回值 operator要重载的运算符
(3)运算符重载基本都是双目运算符,符号左边的是this,右边的是other;意思是运算符调用的是左边对象的运算符重载函数,右边的对象作为传参;
(4)比如:c = a + b 等同于 a.operator+(b); c = b + a 等同于 b.operator+(a);

4.2、代码运行结果

[root#]$ ./app 
(1, 3)
(2, 6)
(3, 9)

5、"+"重载后a+b一定等于b+a?

coordinate coordinate::operator+(const coordinate& other)
{
	tmp.x = this->x + other.x * 2;
	tmp.y = this->y + other.y * 2;
	
	return tmp;
}

(1)a+b不一定等于b+a,这取决于我们运算符重载函数是如何定义的;
(2)在上面的代码中,将"+"运算符重载函数改成上面的形式,a+b就不等于b+a;
(3)a + b 等同于 a.operator+(b),其中this是a,other是b;b + a 等同于 b.operator+(a),其中this是b,other是a;

6、拷贝构造函数和"="赋值运算符重载的区别

6.1、示例代码

#include <iostream>

using namespace std;

class coordinate
{
public:
	int x;					// x轴坐标
	int y;					// y轴坐标
	
	coordinate();
	coordinate(int x0, int y0);
	
	// 拷贝构造函数
	coordinate(const coordinate& rhs);
	
	void print(void);
	
	// =的运算符重载函数
	coordinate& operator=(const coordinate& other);

};

coordinate::coordinate()
{
	x = 0;
	y = 0;
};

coordinate::coordinate(int x0, int y0)
{
	cout << "coordinate construtor" <<endl;
	x = x0;
	y = y0;
};

// 拷贝构造函数
coordinate::coordinate(const coordinate& src)
{
	cout << "---copy coordinate construtor---" << endl;
	this->x = src.x;
	this->y = src.y;
}

void coordinate::print(void)
{
	cout << "(" << this->x << ", " << this->y << ")" << endl;
}

coordinate& coordinate::operator=(const coordinate& other)
{
	cout << "operator=" << endl;

	this->x = other.x;
	this->y = other.y;
	
	return *this;
}

int main(void)
{
	//普通的构造函数
	coordinate a(1, 2);

	//拷贝构造函数
	coordinate b = a;

	// =运算符重载函数
	coordinate c;
	c = a;

	a.print();
	b.print();
	c.print();

	return 0;
}

6.2、代码执行结果

[root#]$ ./app 
coordinate construtor
---copy coordinate construtor---
operator=
(1, 2)
(1, 2)
(1, 2)

(1)在定义对象的同事用同类型的对象来初始化,调用的是拷贝构造函数;
(2)定义好对象后,再用同类型的对象来赋值,此时调用的是"="运算符重载函数;

7、赋值运算符重载返回引用和对象的区别

(1)编译器并没有规定运算符重载函数的返回值,可以返回对象、引用、指针或者void;
(2)如果重载函数返回值是void,则重载的函数不支持连续操作,比如:连加操作、连续赋值操作;
(3)如果重载函数返回值是对象,则在连续操作时是值传递,涉及对象不停的复制,比较消耗资源;
(4)如果重载函数返回值是对象,则在连续操作时不涉及对象的复制,节省资源;

8、友元函数实现运算符重载

8.1、示例代码

#include <iostream>

using namespace std;

class coordinate
{
public:
	int x;					// x轴坐标
	int y;					// y轴坐标
	
	coordinate(void){	x = 0;	y = 0;};
	coordinate(int x0, int y0);
	
	void print(void);

	//友元函数
	friend coordinate operator+(const coordinate& a, const coordinate& b);
	
};

coordinate::coordinate(int x0, int y0)
{
	x = x0;
	y = y0;
};

void coordinate::print(void)
{
	cout << "(" << this->x << ", " << this->y << ")" << endl;
}

// 用独立函数来实现+的运算符重载
coordinate operator+(const coordinate& a, const coordinate& b)
{
	coordinate tmp;
	
	tmp.x = a.x + b.x;
	tmp.y = a.y + b.y;
	
	return tmp;
}


int main(void)
{
	coordinate a(1, 3);
	coordinate b(2, 4);
	coordinate c;
	
	c = a + b;	// 相当于是 c = operator+(a, b);
	
	a.print();
	b.print();
	c.print();

	return 0;
}

(1)之前的代码实现运算符重载,重载函数都是属于某个类的,上面的示例代码是用友元函数来实现运算符重载;
(2)友元函数实现运算符重载,因为友元函数不属于类本身,也就不能使用this指针,所以需要将两个操作数都传递进去;

8.2、友元函数和成员函数实现运算符重载的选择?

(1)两种方式C++都是支持的,功能上都可以实现,运算符选择任意一种来实现运算符重载都是可以的;
(2)一般建议将双目运算符重载用友元函数实现,单目运算符重载用成员函数实现;
(3)双目运算符用成员函数实现重载:两种操作数的地位其实是不相等的,运算符左侧的操作是用this指针表示,右侧的操作数是传进去的参数,地位不相等,容易造成a+b不等于b+a;
(4)单目运算符用成员函数实现重载:因为单目运算符只涉及一个操作对象,直接使用成员函数的this指针就可以实现功能,很方便;

;