【C++】运算符重载关于const的分析(超详细)

文章 Tip — 本文内容谨慎阅读

评论中有同学指出本文的错误,本文章是我早年初学编程所理解的一些内容,可能会存在一些误区。

由于学业繁忙暂时不对文章做修订,但又怕误导一些同学。

暂且先将这位同学的解释放在这里:关于文章的对错请大家自行辩证,不要被我以前的文章误导。
在这里插入图片描述

正文开始

C++里面的运算符重载细节特别多,这里用一个 Point 类来演示一下C++的运算符重载。(需要理解友元函数,指针,引用等概念

如下是一个 Point 类,只有 m_x ,m_y 两个私有成员变量
拥有一个构造函数,接收了 x,y 两个参数,
diplay() 方法用来展示 Point 对象的属性。

class Point {
private:
	int m_x, m_y;
public:
	Point(int x, int y) : m_x(x), m_y(y) {} // 参数列表初始化构造函数
	void display() {
		cout << "(x1=" << m_x << ", x2=" << m_y << ")" << endl;
	}
}

重载运算符“+”

class Point {
	int m_x, m_y;
public:
	Point(int x, int y) : m_x(x), m_y(y) {}
	void display() {
		cout << "(x1=" << m_x << ", x2=" << m_y << ")" << endl;
	}
	// 第一个const表示返回值是个常量,用来防止 (p1+p2)=Point(10,20); 这种赋值
	// 括号内第二个const表示传入的参数可以接收const也可以接收非const
	// 第三个const表示常量可以调用该函数	
	// 具体见下面的分析。
	const Point operator+(const Point &point) const {
		return Point(m_x + point.m_x, m_y + point.m_y);
	}
}
const Point operator+(const Point &point) const{
	return Point(m_x + point.m_x, m_y + point.m_y);
}

这个函数定义有 3 个const,分别理解一下它们的意思。

第1个const

第一个const,表示返回值是常量,什么意思呢?我们来看一段C++代码。

int a = 1;
int b = 2;
(a + b) = 5; // 在C++中不允许如此做,会报错

上述这段代码在C++中是不允许的,a,b都是变量,但是无法对(a+b)进行赋值

Point p1 = Point(10, 20);
Point p2 = Point(20, 30);
(p1 + p2) = Point(40, 50); // 如果不加第1个const,则这里不会报错

因此,对我们自定义的 Point 类来说,上面这段代码是应当是不允许的。解决方案是,我们将 (p1+p2) 的结果看作常数,常数不允许被赋值。因此,在重载操作数+的过程中,我们需要将返回值写成 const Point

第2个const

const Point operator+(const Point &point) const{
	//code...
}

第2个 const ,即括号里的const Point &point,这个 const 是什么意思呢?
这个 const 是为了扩大接受参数的范围。我们依旧来看一段C++代码。

int a = 1;  // 变量
const int b = 2; // 常数
// b = 3; // 会报错,常数不可修改
int c = a + b ;  // 常数不可修改,但是可以做运算
// 注意,这里a是变量,b是常数

上面这段代码表示,+ 号可以接收常数参数, 想想也很好理解,常数不可修改,但是可以参与运算。

Point p1 = Point(10, 20);
const Point p2 = Point(20, 30);
Point p3 = p1 + p2; // 不加第2个const,这里会报错,因为p2是常数
// 注意,这里p1是变量,p2是常量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 将p2视为了参数,即将常量视为参数。

对于我们自定义的类来说,上面这段代码应当是被允许的。我们需要在重载操作数+的过程中,给接收的参数加上 const,就可以同时接受 const参数 与 非const参数,扩大了接受参数的范围。

第3个const

const Point operator+(const Point &point) const{
	//code...
}

第3个 const,即括号后面的 const,它的作用是 使得该函数可以被 const 对象所调用。直接看 Point 的操作。

const Point p1 = Point(10, 20);
Point p2 = Point(20, 30);
Point p3 = p1 + p2; // 不加第3个const,这里会报错,因为p1是常数
// 注意,这里p1是常量,p2是变量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 由p1调用的operator+函数,因此调用该函数的是个常数。

请仔细对比第2个const与第3个const的区别,仔细看注释!可能有点容易混淆!
p1对象是个常量,Point p3 = p1 + p2; 实际上等价于 p1.operator+(p2);,实际上是拿常量p1调用了函数,因此这个函数必须可以被常量调用,这就是第3个const的作用。

重载运算符“-”

重载运算符 “-” 与 “+” 思路一模一样,同上。

const Point operator-(const Point &point) const{
	return Point(this->m_x - point.m_x, this->m_y - point.m_y);
}

重载运算符“-”(与上面的含义不同)

我们来看一段C语言代码,理解一下这个“-”的含义是什么。

int a = 2;
int b = -a;

知道了吧,这个“-”,是放在变量前面,让他的值变成相反数。
但是要注意这是个单目运算符,是不需要传函数参数的

const Point operator-() const {
	return Point(-m_x, -m_y);
}

重载运算符 “+=” 与 “-=”

Point& operator+=(const Point &point) { // 返回值要注意
	this->m_x += point.m_x;
	this->m_y += point.m_y;
	return *this;
}

Point& operator-=(const Point &point) { // 返回值要注意
	this->m_x -= point.m_x;
	this->m_y -= point.m_y;
	return *this;
}

这里说一下返回值为什么是 Point &,这是一个引用对象。我们看一段C++代码。

int a = 1;
(a += 1) = 2;

在 C++ 中,(a+=1)是允许被赋值的,我们可以理解为 (a+=1) 的返回值依旧是一个变量
务必与之前的(a+b) = 3区分开,这个式子是不允许的,因此(a+b)的返回值被我们理解为常量,这也是之前3个const之第1个const的原因。
那么就可以理解为什么要用 Point & 来接受参数了。其实还有一个好处,即可以少创建一个对象。(这里不做详细说明)。

重载运算符 “==” 与 “!=”

bool operator==(const Point &point) const {
	return point.m_x == this->m_x && point.m_y == this->m_y;
}
bool operator!=(const Point &point) const {
	return point.m_x != this->m_x || point.m_y != this->m_y;
}

这两个应该很好理解,不做过多说明。

重载运算符 “++” 与 “–”

众所周知,C++中的 ++ 与 – 分为前缀后缀两种,如下。

int a;
a++;
++a;

这两种操作符是需要分别重载的,那么如何区分呢?主要看传参入的参数,如下。

Point& operator++() {} // 默认代表重载++a
const Point operator++(int) {} // 传入参数写int代表a++,固定格式。

Point& 与 const 的分析方法与最开始所讲差不多,理解了那个必然能理解下面的的代码。

Point& operator++() { // ++a
	++m_x;
	++m_y;
	return *this;
}
const Point operator++(int) { // a++
	Point old = Point(m_x, m_y);
	m_x++;
	m_y++;
	return old;
}
Point& operator--() { // --a
	--m_x;
	--m_y;
	return *this;
}
const Point operator--(int) { // a--
	return Point(m_x--, m_y--);
}

重载 “<<" 与 “>>”

注意,重载 “<<”,由于格式是cout << a;,"<<“符号左边的参数必须接收"cout”,因此不可以当作成员函数来重载,必须写在全局区,可以通过与友元函数相结合来达到目的

重载">>“,格式是cin >> a;,”>>"符号左边的参数必须接收 “cin”,同样不可以当作成员函数来重载,必须写在全局区,通过与友元函数来达到目的。
并且,由于接收的参数是会修改的,不能加 const 修饰。

class Point {
 	// 重载 "<<" 与 ">>" 的友元函数
	friend ostream &operator<<(ostream &cout, const Point &point);
	friend istream &operator>>(istream &cin, Point &point);
	int m_x, m_y;
public:
	Point(int x, int y) : m_x(x), m_y(y) {}
}
// 重载 "<<"
ostream &operator<<(ostream &cout, const Point &point) { // 重载 "<<",由于结果是 cout << a; 不可以当作成员函数来重载,必须写在全局区。
	cout << "(x1=" << point.m_x << ", x2=" << point.m_y << ")";
	return cout;
}
// 重载 ">>"
istream &operator>>(istream &cin, Point &point) { // 重载">>",由于接收的参数会修改,不能加const修饰
	cin >> point.m_x >> point.m_y;
	return cin;
}

完整源代码

#include<iostream>
using namespace std;

class Point {
	//friend Point add(Point, Point); // 友元函数
	//friend Point operator+(const Point &, const Point &);
	friend ostream &operator<<(ostream &cout, const Point &point);
	friend istream &operator>>(istream &cin, Point &point);
	int m_x, m_y;
public:
	Point(int x, int y) : m_x(x), m_y(y) {}
	void display() {
		cout << "(x1=" << m_x << ", x2=" << m_y << ")" << endl;
	}
	const Point operator+(const Point &point) const { // 第一个const表示返回值是个常量,用来防止 (p1+p2)=Point(10,20); 这种赋值
		return Point(m_x + point.m_x, m_y + point.m_y);// 括号内第二个const表示传入的参数可以接收const也可以接收非const
	}													// 第三个const表示常量可以调用该函数
	const Point operator-(const Point &point) const{
		return Point(this->m_x - point.m_x, this->m_y - point.m_y);
	}
	Point& operator+=(const Point &point) { // 返回值要注意
		this->m_x += point.m_x;
		this->m_y += point.m_y;
		return *this;
	}
	bool operator==(const Point &point) const {
		return point.m_x == this->m_x && point.m_y == this->m_y;
	}
	bool operator!=(const Point &point) const {
		return point.m_x != this->m_x || point.m_y != this->m_y;
	}
	const Point operator-() const {
		return Point(-m_x, -m_y);
	}

    /* // 这样写也可以达到效果,但是会多创建一个对象,不如下面的写法好
	Point operator++() { // ++a
		return Point(++m_x, ++m_y);
	}*/
	Point& operator++() { // ++a
		++m_x;
		++m_y;
		return *this;
	}

	/*const Point operator++(int) { // a++
		return Point(m_x++, m_y++);
	}*/
	const Point operator++(int) {
		Point old = Point(m_x, m_y);
		m_x++;
		m_y++;
		return old;
	}

	Point& operator--() { // --a
		--m_x;
		--m_y;
		return *this;
	}
	const Point operator--(int) { // a--
		return Point(m_x--, m_y--);
	}
};

ostream &operator<<(ostream &cout, const Point &point) { // 重载 "<<",由于结果是 cout << a; 不可以当作成员函数来重载,必须写在全局区。
	cout << "(x1=" << point.m_x << ", x2=" << point.m_y << ")";
	return cout;
}

istream &operator>>(istream &cin, Point &point) { // 重载">>",由于接收的参数会修改,不能加const修饰
	cin >> point.m_x >> point.m_y;
	return cin;
}

//const Point operator+(const Point &p1, const Point &p2) { // 建议加const,并且设定为引用类型
//	return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
//}

int main() {
	const Point p1(10, 20);
	Point p2(10, 30);
	Point p3(30, 40);


	cin >> p2 >> p3;
	cout << p2 << p3 << endl;

	//Point p4 = --p2 + Point(10, 20);
	//Point p4 = ++p2 + Point(10, 20);
	//p4.display(); // p2++ 20 50    ++p2 21 51
	//p2.display(); // 11 31

	/*p2++;
	(++p2) = p1;
	(++p2)++;*/
	//(p2++) = p1; //不可以
	//p2.display();

	//Point p4 = -p3;
	//p4.display();

	//Point p4 = p1 + p2 + p3;
	//p1 += p2;
	//(p1 += p2)+=p3;
	//p1.display();

	//cout << (p1 == p2) << endl;

	//p4.display();

	//Point p4 = add(p1, p2);
	//p4.display();

	return 0;
}
  • 165
    点赞
  • 396
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌宅鹿同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值