🎈个人主页:库库的里昂
✨收录专栏:C++从练气到飞升
🎉鸟欲高飞先振翅,人求上进先读书。
一、运算符重载的引用
本章将以日期类为例进行展开叙述
通常比较两个操作数的大小,会写成下述方式:
int main() { int i = 1, j = 2; i < j; return 0; }
但是对于日期类采用上述方式会发生报错:
class Date { public: Date(int year = 2023, int month = 9, int day = 25) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "/" << _month << "/" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2023, 10, 1); Date d2(2022, 2, 15); d1.Print(); d2.Print(); d1 < d2; return 0; }
🌟因为日期类是我们自己定义的,属于一种自定义类型,它的大小比较方式,编译器是不知道的,而像int等内置类型,这是原生语言定义的,知道规则来比较,可以直接用<、>去比较两个内置类型变量的大小,但自定义类型不可以哦
最常见的解决方式:
🌟创建一个函数来通过一 一对比年月日来实现日期类的比较,不过会出现关于私有的问题,这个函数是写在类外面的,意味着,日期类的成员变量是private私有的,在类外面就无法访问,所以在这个函数里面是访问不到类对象中的_year、_month、_day,所以x1._year等都是错误的会发生报错,要想实现该函数的功能,可以采用下面3种方法:
class Date { public: Date(int year = 2023, int month = 9, int day = 25) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "/" << _month << "/" << _day << endl; } //private: int _year; int _month; int _day; }; bool DateLess(const Date& x1, const Date& x2) { if (x1._year < x2._year) { return true; } else if (x1._year == x2._year && x1._month < x2._month) { return true; } else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day) { return true; } else { return false; } } int main() { Date d1(2023, 10, 1); Date d2(2022, 2, 15); d1.Print(); d2.Print(); DateLess(d1, d2); return 0;
🌟但是对于这种函数写法相比于直观的<、>来看显然不是很直观,对于不清楚的other来说,读代码是很困难的,所以就引入了运算符重载
二、运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
bool operator<(const Date& x1, const Date& x2) { if (x1._year < x2._year) { return true; } else if (x1._year == x2._year && x1._month < x2._month) { return true; } else if (x1._year == x2._year && x1._month == x2._month&&x1._day<x2._day) { return true; } else { return false; } } int main() { Date d1(2023, 10, 1); Date d2(2022, 2, 15); //cout<<d1<d2<<endl; 至于为什么不写成cout<<d1<d2<<endl是因为<<的优先级高于< d1<d2与operator<(d1<d2)本质上都是调用运算符重载,所以两者写法是等价的只不是一个显示调用,一个没有显示调用 cout << (d1 < d2) << endl; cout << (operator<(d1, d2)) << endl; return 0; }
🌟上述代码就是对<运算符的一个重载,此时两个日期类对象就可以直接用<来比较大小,d1 < d2本质上就是调用运算符重载函数,此外,还需要解决一个问题:上面的运算符重载函数是写在类外面的,日期类的成员变量是private私有的,该运算符重载函数还是不能用。
🌟注意:
- 🌏不能通过连接其他符号来创建新的操作符:比如operator@
- 🌏重载操作符必须有一个自定义类型参数
(int x1, int x2)都是内置类型是不可以的规定必须有一个自定义类型的参数 也就是说只能对自定义类型进行重载,内置类型不可以 bool operator<(int x1, int x2) { if (x1._year < x2._year) { return true; } else if (x1._year == x2._year && x1._month < x2._month) { return true; } else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day) { return true; } else { return false; } }
- 🌏用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
- 🌏作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
d1<d2两个操作数是不可以随意换位置的,左操作数就是第一个参数(this),右操作数就是第二个参数(d) d1.operator<(d2) bool operator==(Date* this, const Date& d2) 这里需要注意的是,左操作数是this,指向调用函数的对象(和上面一个意思) bool operator<(const Date& d) { if (_year < d._year) { return true; } else if (_year == d._year && _month < d._month) { return true; } else if (_year == d._year && _month == d._month && _day < d._day) { return true; } else { return false; } }
🌟注意:当运算符重载函数写成类成员函数和在类外面定义调用的时候是不一样的
类成员函数 bool operator<(const Date& d) { if (_year < d._year) { return true; } else if (_year == d._year && _month < d._month) { return true; } else if (_year == d._year && _month == d._month && _day < d._day) { return true; } else { return false; } } int main() { Date d1(2023, 10, 1); Date d2(2022, 2, 15); cout << (d1 < d2) << endl; cout << (d1.operator<(d2)) << endl; 成员函数---符合调用规则因为有一个是隐含的参数(d1.operator(d2)) return 0; } —————————————————————————————————————————————————————————————————————————————————— 类外面定义函数 bool operator<(const Date& x1, const Date& x2) { if (x1._year < x2._year) { return true; } else if (x1._year == x2._year && x1._month < x2._month) { return true; } else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day) { return true; } else { return false; } } int main() { Date d1(2023, 10, 1); Date d2(2022, 2, 15); cout << (d1 < d2) << endl; cout << (operator<(d1, d2)) << endl; return 0; }
- 🌏不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个参数
- 🌏(.* 和 *不一样 * 是可以重载的) 、 (域作用限定符 ::)、 sizeof 、(三目运算符?:) 、(对象变量取成员 .) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现选择题。
三、赋值运算符重载
🌟 首先要区分赋值运算符和拷贝构造:
- 赋值:两个已经存在的对象进行拷贝
- 拷贝构造:一个已经存在的对象去初始化另一个对象
Date d1(2023, 10, 1); Date d2(2023, 10, 7); d1 = d2; --->调用赋值运算符重载 Date d3 = d1; --->调用拷贝构造函数;或者写成这种也是拷贝构造Date d3(d1);
1 .赋值运算符重载格式:
- 参数类型:const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this :要复合连续赋值的含义
Date& operator=(const Data& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; }
2 .赋值运算符只能重载成类的成员函数不能重载成全局函数:
我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数。
class Date { public: Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } int _year; int _month; int _day; }; 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数 Date& operator=(Date& left, const Date& right) { if (&left != &right) { left._year = right._year; left._month = right._month; left._day = right._day; } return left; }
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
3 .用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝:
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
【C++从练气到飞升】05---运算符重载(二)+https://developer.aliyun.com/article/1502592