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,分别理解一下它们的意思。
第一个const,表示返回值是常量,什么意思呢?我们来看一段C++代码。
int a = 1;
int b = 2;
(a + b) = 5; // 在C++中不允许如此做,会报错
上述这段代码在C++中是不允许的,a,b都是变量,但是无法对(a+b)进行赋值。
Point p1 = (10, 20);
Point p2 = (20, 30);
(p1 + p2) = Point(40, 50); // 如果不加第1个const,则这里不会报错
因此,对我们自定义的 Point 类来说,上面这段代码是应当是不允许的。解决方案是,我们将 (p1+p2) 的结果看作常数,常数不允许被赋值。因此,在重载操作数+的过程中,我们需要将返回值写成 const Point
。
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 = (10, 20);
const Point p2 = (20, 30);
Point p3 = p1 + p2; // 不加第2个const,这里会报错,因为p2是常数
// 注意,这里p1是变量,p2是常量,由于重载了“+”
// 上一句代码等同于 p1.operator+(p2);
// 将p2视为了参数,即将常量视为参数。
对于我们自定义的类来说,上面这段代码应当是被允许的。我们需要在重载操作数+的过程中,给接收的参数加上 const,就可以同时接受 const参数 与 非const参数,扩大了接受参数的范围。
const Point operator+(const Point &point) const{
//code...
}
第3个 const,即括号后面的 const,它的作用是 使得该函数可以被 const 对象所调用。直接看 Point 的操作。
const Point p1 = (10, 20);
Point p2 = (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
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;
}