1、运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也是具有其返回值类型,函数名字以及参数列表,其返回值类型和参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号;
函数原型:返回值类型 operator操作符(参数列表)。
注意:
1.不能通过链接其他符号来创建新的操作符:比如 operator@;
2.重载操作符必须有一个类类型参数;
3.用于内置类型的运算符,其含义不能改变,例如:内置类型的+,不能改变其含义;
4.作为类成员函数重载时,其形参看起来比操作书数数目少1,因为成员函数的第一个参数为隐藏的this;
5. .* :: sizeof ?: . 注意以上五个运算符不能重载,这个经常在笔试选择题中出现。
//运算符重载 bool operator<(const Date& d); bool operator==(const Date& d); bool operator<=(const Date& d); bool operator>(const Date& d); bool operator>=(const Date& d); bool operator!=(const Date& d); //函数实现 bool Date::operator<(const Date& d) { if (_year < d._year) return true; else if (_year == d._year) { if (_month < d._month) return true; else if (_month == d._month) return _day < d._day; } return false; } bool Date::operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } 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); } bool Date::operator!=(const Date& d) { return !(*this == d); }
当我们重载了 < 和 == 时,就可以复用这两个运算符,重载<= > >= != ,更加方便了。
2、赋值运算符重载
1.参数类型:const 参数名&,传递引用可以提高传参效率,(不用再调用拷贝构造了);
2.返回值类型:参数名& 返回引用可以提高返回的效率,有返回值的目的是为了支持连续赋值;
3.检测是否自己给自己赋值
4.返回this,要符合连续赋值的含义;
//声明 Date& operator=(const Date& d); //定义 Date& Date:: operator=(const Date& d) { _year = d._year; _month = d._month; _day = d._day; return *this; }
2、赋值运算符只能重载成成员函数,不能重载成全局函数
因为赋值运算符第一个参数是this指针,重载成全局函数,就需要传this指针,而当类里面没有显式定
义赋值运算符时,编译器会自动生成一个默认的。此时就会和类外的赋值运算符重载形成冲突,所以赋值运算符只能是成员函数。
3、用户没有显式实现时,编译器会自动生成一个默认的赋值操作符重载,以值的方式逐字节拷贝
注意:内置类型成员变量是直接赋值的,而自定义成员变量需要调用对应类的赋值运算符重载完成赋值,如果自定义类中没有显式实现赋值运算符重载,编译器也会默认生成赋值重载;
class Time { public: Time() { _hour = 1; _minute = 1; _second = 1; } //这个赋值运算符重载写不写都可以,不写的话编译器也会自动生成 /*Time& operator=(const Time& t) { if (this != &t) { _hour = t._hour; _minute = t._minute; _second = t._second; } return *this; }*/ //private: int _hour; int _minute; int _second; }; class Date1 { public: void print() { cout << _year << "-" << _month << "-" << _day << endl; } private: // 基本类型(内置类型) int _year = 1970; int _month = 1; int _day = 1; // 自定义类型 Time _t; }; int main() { Date1 d1; Date1 d2; d1 = d2; d1.print(); d2.print(); return 0; }
但是,我们真的不需要自己写了吗?
不是的,如果类中涉及到资源管理,开辟空间的,就要自己实现赋值重载了,因为编译器自己实现的是浅拷贝,也就是值拷贝,不会额外开辟空间,所以我们自己写深拷贝,和拷贝构造,析构函数差不多。
3、前置++和后置++重载
前置++,返回+1之后的结果,
注意:this指向的对象函数结束后不会销毁,故用引用的方式返回提高效率;
后置++,返回+1之前的结果
为了能够区分
C++规定:后置++重载时多增加一个Int 参数,但调用函数时,用户不用传递,编译器会自动传递,(问就是C++规定的)
后置++,要用值的方式返回,因为要在函数内创建一个临时对象tem来保存*this,然*this++
然后返回tem,
//前置++ Date& Date::operator++() { *this += 1; return *this; } //后置++ //int 只是一个标志,代表他是后置的--,没有实际意义 Date Date::operator++(int) { Date tem = *this; *this += 1; return tem; } //前置-- Date& Date::operator--() { *this -= 1; return *this; } //后置-- Date Date::operator--(int) { Date tem = *this; *this -= 1; return tem; }
4、const成员变量
将const修饰的成员函数成为“const成员函数”,const修饰类成员函数,实际上是修饰该类成员函数隐函的 this 指针,表明在该成员函数中不能对类的成员进行修改。
class Date1 { public: Date1(int year, int month, int day) { _year = year; _month = month; _day = day; } void Print() { cout << "Print()" << endl; cout << "year:" << _year << endl; cout << "month:" << _month << endl; cout << "day:" << _day << endl << endl; } void Print() const { cout << "Print()const" << endl; cout << "year:" << _year << endl; cout << "month:" << _month << endl; cout << "day:" << _day << endl << endl; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; void Test() { Date1 d1(2022, 1, 13); d1.Print(); const Date1 d2(2022, 1, 13); d2.Print(); } int main() { Test(); return 0; }
注意:权限可以缩小,平移,但是不可以放大;
请思考下面的几个问题:
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?
5、日期类的实现
我们还可以重载流插入和流提取
流插入我们要在类外实现,因为在类中实现,第一个参数是隐形的this指针,而我们希望第一个参数
是ostream& out,那我们就定义在类外可以解决这个问题,但是定义在类外我们就无法访问类中的私有的成员变量,就用到了另一个办法,友元,友元的概念就是我是你的朋友,我可以访问你的元素,不管是公有还是私有,这里暂且了解一下,下节会讲;
下面来看日期类的实现,上面的运算符重载都会用到;
//Date.h #pragma once #include<iostream> using namespace std; int is_year(int y); class Date { friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d); public: Date(int year=1 ,int month=1, int day=1); Date(const Date& d); //在类里面定义的函数默认就内联函数 int GetMonthDay(int y, int m) { static int months[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (m == 2) return months[m] + is_year(y); return months[m]; } //日期+=天数 Date& operator+=(int day); Date operator+(int day); Date& operator-=(int day); Date operator-(int day); //++和-- Date& operator++(); Date operator++(int); Date& operator--(); Date operator--(int); //日期-日期 int operator-(const Date& d); //运算符重载 Date& operator=(const Date& d); bool operator<(const Date& d); bool operator==(const Date& d); bool operator<=(const Date& d); bool operator>(const Date& d); bool operator>=(const Date& d); bool operator!=(const Date& d); //流插入和流输出 ~Date(); void print(); private: int _year; int _month; int _day; }; ostream& operator<<(ostream& out, const Date& d); istream& operator>>(istream& in, Date& d); 2.
//Date.cpp #include"Date.h" int is_year(int y) { if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) return 1; return 0; } Date::Date(int year , int month , int day ) { _year = year; _month = month; _day = day; } Date::Date(const Date& d) { cout << "Date::Date(const Date& d)" << endl; _year = d._year; _month = d._month; _day = d._day; } Date& Date::operator+=(int day) { _day += day; while (_day > GetMonthDay(_year, _month)) { _day -= GetMonthDay(_year, _month); _month++; if (_month == 13) { _year++; _month = 1; } } return *this; } Date Date::operator+(int day) { Date tem = *this; tem += day; return tem; } Date& Date::operator-=(int day) { _day -=day; while (_day < 0) { _month--; if (_month == 0) { _year--; _month = 12; } _day += GetMonthDay(_year, _month); } return *this; } Date Date::operator-(int day) { Date tem = *this; tem -= day; return tem; } //前置++ Date& Date::operator++() { *this += 1; return *this; } //后置++ //int 只是一个标志,代表他是后置的--,没有实际意义 Date Date::operator++(int) { Date tem = *this; *this += 1; return tem; } //前置-- Date& Date::operator--() { *this -= 1; return *this; } //后置-- Date Date::operator--(int) { Date tem = *this; *this -= 1; return tem; } //日期-日期 int Date:: operator-(const Date& d) { Date max = *this; Date min = d; int n = 0; int flag = 1; if (max < min) { max = d; min = *this; flag = -1; } while (min!=max) { ++min; ++ n; } return n*flag; } //赋值运算符重载 Date& Date:: operator=(const Date& d) { _year = d._year; _month = d._month; _day = d._day; return *this; } bool Date::operator<(const Date& d) { if (_year < d._year) return true; else if (_year == d._year) { if (_month < d._month) return true; else if (_month == d._month) return _day < d._day; } return false; } bool Date::operator==(const Date& d) { return _year == d._year && _month == d._month && _day == d._day; } 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); } bool Date::operator!=(const Date& d) { return !(*this == d); } void Date::print() { cout << _year << "-" << _month << "-" << _day << endl; } Date::~Date() { //cout << "Date::~Date()" << endl; } ostream& operator<<(ostream& out, const Date& d) { out << d._year << "年" << d._month << "月" << d._day << endl; return out; } istream& operator>>(istream& in, Date& d) { cout << "请输入日期:" << endl; in >> d._year >> d._month >> d._day; return in; }
//Test.cpp #include"Date.h" //Date func() //{ // Date d3(2024, 4, 14); // return d3; //} int fx() { int a = 10; int b = 20; int c = 30; return a + b + c; } //int main() //{ // // Date ret = func(); // ret.print(); // // // /*Date d1(2024, 4, 14); // Date d2(2024, 5, 14); // d1.print(); // d2.print(); // cout << (d2 < d1) << endl; // cout << (d2 <= d1) << endl; // cout << (d2 > d1) << endl; // cout << (d2 >= d1) << endl; // cout << (d2 == d1) << endl; // cout << (d2 != d1) << endl;*/ // // return 0; //} //Date func() //{ // Date d3(2024, 4, 14); // return d3; //} //Date& func() //{ // Date d3(2024, 4, 14); // return d3; //} //int main() //{ // //const Date& ret = func(); // //ret.print(); // // return 0; //} //Date& func() //{ // static Date d3(2024, 4, 14); // return d3; //} //int main() //{ // Date& ret = func(); // ret.print(); // // return 0; //} //class Time //{ //public: // Time() // { // _hour = 1; // _minute = 1; // _second = 1; // } // /*Time& operator=(const Time& t) // { // if (this != &t) // { // _hour = t._hour; // _minute = t._minute; // _second = t._second; // } // return *this; // }*/ // private: // int _hour; // int _minute; // int _second; //}; // //class Date1 //{ //public: // void print() // { // cout << _year << "-" << _month << "-" << _day << endl; // } //private: // // 基本类型(内置类型) // int _year = 1970; // int _month = 1; // int _day = 1; // // 自定义类型 // Time _t; //}; //int main() //{ // Date1 d1; // Date1 d2; // d1 = d2; // // d1.print(); // d2.print(); // return 0; //} //int main() //{ // Date d1(2024, 4, 15); // Date d2(2024, 2, 15); // d2 = d1; // // d1.print(); // d2.print(); // // // return 0; //} //class Date1 //{ //public: // Date1(int year, int month, int day) // { // _year = year; // _month = month; // _day = day; // } // void Print() // { // cout << "Print()" << endl; // cout << "year:" << _year << endl; // cout << "month:" << _month << endl; // cout << "day:" << _day << endl << endl; // } // void Print() const // { // cout << "Print()const" << endl; // cout << "year:" << _year << endl; // cout << "month:" << _month << endl; // cout << "day:" << _day << endl << endl; // } //private: // int _year; // 年 // int _month; // 月 // int _day; // 日 //}; //void Test() //{ // Date1 d1(2022, 1, 13); // d1.Print(); // const Date1 d2(2022, 1, 13); // d2.Print(); //} // int main() //{ // Test(); // return 0; //} int main() { Date d1(2024, 4, 17); Date d2(2024, 9, 14); cin >> d1 >> d2; cout << d1 << d2; /*cout << (d2 - d1) << endl; d1.print(); d2.print();*/ return 0; }
6、取地址及const取地址操作符重载
这两个默认构造函数一般不用重新定义,编译器会默认生成;
class Date1 { public: Date1* operator&() { return this; } const Date1* operator&()const { return this; } private: int _year; int _month; int _day; };
这两个运算符一般不需要重载,使用编译器默认生成的取地址重载即可,除非特殊情况,比如想让别人获取到指定的内容!