- 5.赋值运算符重载
- 5.1 运算符重载
- 5.2 赋值运算符重载
- `Date.cpp`
- `Date.h`
- `test.cpp`
- 7.const成员
- 8.取地址及const取地址操作符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意:
-
不能通过连接其他符号来创建新的操作符:比如operator@重载操作符必须有一个类类型参数
-
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
-
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
-
.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
class Date { public: Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } //写一个公有的函数避免在类外不能使用的情况——麻烦 //int GetYear() //{ // return _year; //} //直接写进类里,但是参数过多 bool operator==(const Date& x) { return _year == x._year && _month == x._month && _day == x._day; } private: int _year; int _month; int _day; }; //bool DateEquel(Date x1, Date x2) //bool func(Date x1, Date x2) //bool riqixiangdeng(Date x1, Date x2) //{ // return x1._year == x2._year // && x1._month == x2._month // && x1._day == x2._day; //} //函数的类型是运算之后的返回值决定的 //bool operator==(Date x1, Date x2)//第一个参数是左操作数,第二个操作数是右操作数 //bool operator==(const Date& x1, const Date& x2) //{ 私有的在类外面不能直接访问 // return x1._year == x2._year // && x1._month == x2._month // && x1._day == x2._day; //} //int operator-(Date x1, Date x2) //{} int main() { Date d1(2022, 7, 23); Date d2(2022, 7, 24); // 内置类型可以直接使用运算符运算,编译器知道要如何运算 // 自定义类型无法直接使用运算法,编译器也不知道要如何运算。想支持,自己实现运算符重载即可 //cout << operator==(d1,d2) << endl;//可以写自定义函数,可读性差 cout << d1.operator==(d2) << endl; // -> d1.operator==(&d1, d2) cout << (d1 == d2) << endl;//编译器会自动转换成cout << operator==(d1,d2) << endl; 自定义类型: //d1 < d2; //d1++; //d1 + 100; //Date d3(2022, 10, 1); //d3 - d2; return 0; } class Date { public: int GetMonthDay(int year, int month) { //静态区——每次访问同一个 static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; //if (month == 2 && IsLeapYear(year)) if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { return 29;//闰年 } else { return days[month]; } } Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } bool operator==(const Date& x) { return _year == x._year && _month == x._month && _day == x._day; } bool operator<(const Date& x); bool operator>(const Date& x); bool operator>=(const Date& x); bool operator<=(const Date& x); bool operator!=(const Date& x); // d1 += 100; Date& operator+=(int day) { _day += day; while (_day > GetMonthDay(_year, _month)) { _day -= GetMonthDay(_year, _month); ++_month; if (_month == 13) { _month = 1; _year++; } } return *this;//this指针就是当前对象 } // d1 + 100;日期加100天还是日期所以类型是date Date operator+(int day) { Date ret(*this); ret._day += day; while (ret._day > GetMonthDay(ret._year, ret._month)) { ret._day -= GetMonthDay(ret._year, ret._month); ++ret._month; if (ret._month == 13) { ret._month = 1; ret._year++; } } return ret; } private: int _year; int _month; int _day; }; int main() { Date d1(2022, 7, 23); //Date d2(2022, 7, 24); //d1 == d2; //d1 < d2; Date ret = d1 + 50; //Date ret(d1 + 50); //d1++; //Date d3(20202, 10, 1); //d3 - d2; return 0; }
- 赋值运算符重载格式
参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值,检测是否自己给自己赋值
返回*this :要复合连续赋值的含义
class Date { public : Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } Date (const Date& d) { _year = d._year; _month = d._month; _day = d._day; } Date& operator=(const Date& d) { if(this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } private: int _year ; int _month ; int _day ; };
- 赋值运算符只能重载成类的成员函数,不能重载成全局函数
原因:
赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数
《C++ prime》中说明我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数
- 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值
6.日期类的实现和前置++和后置++重载
Date.cpp#define _CRT_SECURE_NO_WARNINGS 1 #include "Date.h" void Date::Print() { cout << _year << "/" << _month << "/" << _day << endl; } // 任何一个类,只需要写一个> == 或者 < ==重载 剩下比较运算符重载复用即可 //类作为一个整体,上下都会搜索,所以谁复用谁的顺序无所谓 bool Date::operator== (const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } // d1 != d2 复用就可以 bool Date::operator!=(const Date& d) { return !(*this == d); } // d1 > d2 bool Date::operator>(const Date& d) { if ((_year > d._year)//年大就大 || (_year == d._year && _month > d._month)//年相等,月大就大 || (_year == d._year && _month == d._month && _day > d._day))//年月想等,天大就大 { return true; } else { return false; } } bool Date::operator>=(const Date& d) { return (*this > d) || (*this == d); } bool Date::operator<(const Date& d) { return !(*this >= d); } bool Date::operator<=(const Date& d) { return !(*this > d); } // d2 += d1 += 100 自己的值也要改变,返回值就是自己 Date& Date::operator+=(int day) { if (day<0) { return *this -= -day; } _day += day; while (_day > GetMonthDay(_year, _month)) { _day -= GetMonthDay(_year, _month); ++_month;//天数超过,月就++ if (_month == 13) { _year++;//月数超过,年就++ _month = 1; } } return *this;//this指针就是当前的自己 } // d1 + 100 自己不改变,返回值是另一个对象 Date Date::operator+(int day) { //Date ret(*this); Date ret = *this;//拷贝构造,不是赋值 ret += day; return ret; } //反过来复用——+=会变低效,拷贝构造会变多 //Date Date::operator+(int day) //{ // Date ret = *this; // // ... // ret._day += day; // while (ret._day > GetMonthDay(ret._year, ret._month)) // { // //... // } // // return ret; //} // d1 += 100 //Date& Date::operator+=(int day) //{ // *this = *this + day; // // return *this; //} Date& Date::operator++() // 前置,返回++之后的 { / private: int _year; int _month; int _day; }; //流插入重载 inline ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; }//写成inline声明和定义不分离 //流提取重载 inline istream& operator>>(istream& in, Date& d) { in >> d._year >> d._month >> d._day; assert(d.CheckDate()); return in; }test.cpp
//菜单 void TestDate2() { const char* WeeDayToStr[] = { "周一", "周二", "周三", "周四", "周五", "周六", "周天" }; Date d1, d2; int day = 0; int option = 0; do{ cout << "*****************************" << endl; cout << " 1.日期加、减天数 2.日期减日期" << endl; cout << " 3.日期->周几 -1.退出" << endl; cout << "*****************************" << endl; cin >> option; cout << "请选择:"; if (option == 1) { cout << "请一次输入日期及天数(减天数需要输入负数):"; cin >> d1 >> day; cout <<"日期加减天数后的日期:"<< d1 + day << endl; } else if (option == 2) { cout << "请依次输入两个日期:"; cin >> d1 >> d2; cout << "相差的天数:" << d1 - d2 << endl; } else if (option == 3) { cout << "请输入日期:"; cin >> d1; Date start(1, 1, 1); int n = d1 - start; int weekDay = 0; // 周一 weekDay += n; //weekDay += 9; //cout << "周" << weekDay % 7 + 1 << endl; cout << WeeDayToStr[weekDay % 7] << endl; } else { cout << "无此选项,请重新选择" << endl; } } while (option != -1); } int main() { TestDate2(); return 0; }
int i = 0; double d = 1.1; cout << i;//cout.operator<<(i); cout << d;//cout.operator<<(d);
- 库里面写好了运算符重载
- 自动识别类型,它们构成函数重载
运算符重载:让自定义类型对象可以用运算符。转换成调用这个重载函数
函数重载:支持函数名相同的函数同时存在
两者虽然都用了重载这个词,但是它们之间没有必然联系。
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
void TestDate6() { Date d1(2022, 7, 25); const Date d2(2022, 7, 25); d1.Print();//&d1 ——> Date* d2.Print();//&d2 ——> const Date* d1 < d2; d2 < d1; } int main() { TestDate6(); return 0; } const本身不能被修改但是有一次初始化的机会 Date*传Date*可以 const Date*传Date*属于权限的放大,不可行 void Date::Print()//Date* const this { cout << _year << "/" << _month << "/" << _day << endl; } bool Date::operator<(const Date& d)///Date* const this { return !(*this >= d); } 修改: void Date::Print() const //const修饰的是this指针指向的内容 { this ->_year = 1;//报错,不能被修改 cout << _year << "/" << _month << "/" << _day << endl; } bool Date::operator<(const Date& d) const { return !(*this >= d); }
第一个也没有问题,因为Date* 传const Date*属于权限的缩小
const修饰的是this指针指向的内容,也就是保证了成员函数内部不会修改成员变量
const对象和非const对象都可以调用这个成员函数
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比
如想让别人获取到指定的内容!
class A { public: // 他们是默认成员函数,我们不写编译器会自动生成,自动生成就够用了,所以一般是不需要我们自己写的 // 特殊场景:不想让别人取到这个类型对象的地址 A* operator&() { return nullptr;//返回空指针或者设为私有 } const A* operator&()const { return nullptr; } void Print() const { //_year = 1; cout << _year << "/" << _month << "/" << _day << endl; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; int main() { A d1; const A d2; d1.Print();//权限的缩小 d2.Print();//权限的平移 cout << &d1 << endl; cout << &d2 << endl; return 0; }