运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数原型:返回值类型 operator操作符(参数列表)
比如日期比较大小:
int main()
{
Date d1(2023,7,23);
Date d2(2022, 8, 21);
d1 < d2;
return 0;
}
不能直接用运算符比较!可以先写一个DateLess函数来比较
bool DateLess(const Date& x1, const Date& x2)
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;
}
}
但是我就是想写d1<d2,应该怎么实现呢?采用运算符重载,这里的函数在类的外面,不是成员函数
bool operator<(const Date& x1, const Date& x2)
bool operator<(const Date& x1, const Date& x2) //operator<就是函数名,不是成员函数,在类的外面
{
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,7,23);
Date d2(2022, 8, 21);
cout << (d1<d2) << endl;
//转换成下面的
cout << (operator<(d1, d2)) << endl;
return 0;
}
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个是自定义类型,比如重载整形不让瞎搞。
3.对于内置类型的运算符,其含义不能改变。例如内置类型的整形,不能改变其含义。
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5 .* :: sizeof ?: .注意以上5个运算符不能重载。
6.不能改变操作符的操作数的个数,比如+是两个操作数,++是一个操作数,一个操作符是几个操作数,那么重载的时候就有几个参数
日期类比较大小,引入成员函数,d1.operator<(d2)
//d1<d2 转换成d1.operator<(d2) d1就是this d2就是d,顺序不能换
bool operator<(const Date& d) //operator可以写成成员函数,但是会出现参数太多了的问题
{
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;
}
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023,7,23);
Date d2(2022, 8, 21);
cout << (d1<d2) << endl;
//转换成下面的
cout << (d1.operator<(d2))<< endl;
return 0;
}
举例:d1<d2,d1==d2,d1<=d2(复用<和==),必须先实现<=之后,> != 才能复用
//d1<d2 转换成d1.operator<(d2) d1就是this d2就是d,顺序不能换
bool operator<(const Date& d) //operator可以写成成员函数,但是会出现参数太多了的问题
{
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 && _month == d._month && _day == d._day)
{
return true;
}
else
{
return false;
}
}
//d1<=d2,this是d1的地址,*this就是d1
bool operator<=(const Date& d) //复用上面
{
return *this < d || *this == d;
}
bool operator>(const Date& d) //<=取反
{
return !(*this <= d);
}
bool operator!=(const Date& d) //==取反
{
return !(*this == d);
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023,7,23);
Date d2(2022, 8, 21);
cout << (d1<d2) << endl;
cout << (d1 == d2) << endl;
cout << (d1 <= d2) << endl;
return 0;
实现日期加天数,进位法。先把日期加到天上,如果小于当前月的天数不进位,要是大,减当前月的天数,月进一位,月满了往年去进。
2023 7 21
50
2023 7 71
-31
2023 8 40
实例:+复用+=
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
int GetMonthDay(int year, int month)//获取某一年某月的天数
{
int monthArray[13] = { 0, 31, 28, 31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ) //如果是闰年并且他还是2月,先判断是不是2月,不然费工夫
{
return 29;
}
return monthArray[month];
}
Date& operator+=(int day) // Date ret = d1 + 100;因为d1也变了,出了作用域*this还在,用传值返回会生成他的拷贝,拷贝要调用拷贝构造,所以用引用返回
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
_year++;
_month = 1;//年加加,月制成来年的一月
}
}
return *this; //this是指针
}
Date operator+(int day) // Date ret = d1 + 100;不能改变d1,所以就拷贝一份d1,这里用+复用+=
{
Date tmp(*this); //拷贝构造
tmp._day += day; //拷贝一份d1,我再去复用+=
return tmp; //拷贝构造
//tmp._day += day;
//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//{
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// ++tmp._month;
// if (tmp._month == 13)
// {
// tmp._year++;
// tmp._month = 1;//年加加,月制成来年的一月
// }
//}
//return tmp; //出了作用域tmp不在,不在了就不能用引用返回
}
private:
int _year;
int _month;
int _day;
};
void TestDate2()
{
Date d1(2023, 7, 23);
d1 += 20000;
d1.Print();
Date d2(2022, 8, 13);
Date ret = d2;//这里是拷贝构造,因为拷贝构造,重点是构造,一个已经存在的对象去初始化另一个要创建的对象
Date ret; //赋值是已经存在的两个对象的拷贝
//这里是赋值重载
ret=d2+20000;
}
int main()
{
Date d1(2023,7,23);
Date d2(2022, 8, 21);
Date ret = d1 += 100;//日期加一个天数有意义,这就是拷贝构造
return 0;
}
+=复用+
Date operator+(int day) // Date ret = d1 + 100;不能改变d1,所以就拷贝一份d1
{
Date tmp(*this); //拷贝
tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= GetMonthDay(tmp._year, tmp._month);
++tmp._month;
if (tmp._month == 13)
{
tmp._year++;
tmp._month = 1;//年加加,月制成来年的一月
}
}
return tmp; //出了作用域tmp不在,不在了就不能用引用返回 拷贝
}
Date& operator+=(int day) //d1+=100
{
*this = *this + day; //d1先调用+,两次拷贝,赋值在是一次拷贝,返回一个Date,在赋值给*this
return *this;
}
但是以上两种哪个更好:
+复用+= 上下的+都是两次拷贝,但是上面的+=没有拷贝,下面的+=两次拷贝和一次赋值
实现-和-=
赋值运算符重载
//d1=d3,左边就是第一个参数,右边就是第二个,this就是第一个 ,把d3赋值给d1
void operator=(const Date& d)//可以不加引用
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void TestDate1()
{
Date d1(2023, 7, 23);
Date d2(d1);//这是拷贝构造,重点是构造,一个已经存在的对象去初始化另一个要创建的对象
d1.Print();
d2.Print();
Date d3(2022, 8, 13);
d1 = d3;//d1.operator=(d3)这是赋值,都是完成拷贝行为,赋值是已经存在的两个对象的拷贝
}
存在一个问题,连续赋值
d1 = d2 = d3; 报错
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this; //如果是传值返回,返回的是d1的拷贝,这里会调用拷贝构造,出了作用域*this还在,所以用引用返回
}
没加引用:传值传参和传值返回不管是内置类型还是自定义类型都要调用拷贝构造,传值传参实参传给形参,形参就是实参的拷贝,传值返回就要生成这个对象的拷贝。 内置类型直接拷贝
Date operator=(const Date d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this; //如果是传值返回,返回的是d1的拷贝,这里会调用拷贝构造,出了作用域*this还在,所以用引用返回
}
d1=d1 ;应该禁止,所以应该以下写法:
Date& operator=(const Date& d)
{
if (this != &d)取地址
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
赋值重载对内置类型浅拷贝,所以日期类可以不写,但是栈类必须要写
注意:拷贝构造!=赋值重载,有的地方需要写赋值重载
实现-=和-
Date& operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (day <= 0) //<=0都不合法,有7月0号吗?
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date operator-(int day)
{
Date tmp(*this); //拷贝
tmp -= day;
return tmp; //拷贝
}
void TestDate3()
{
Date d1(2023, 7, 23);
d1 -= -100; //-100所以就是日期判 -=-100 就是+=+100
}
实现d1++和++d1
++d1->d1.operator++() 返回的是++之后的
Date& operator++()
d1++->d1.operator++(0)返回++之前的值
Date& operator++(int)加了一个int参数进行占位,跟前置++构成函数重载进行区分;本质后置调用,编译器进行特殊处理。
注意:函数重载是函数名相同,参数类型不同;运算符重载是为了自定义类型能去用运算符。
Date& operator++() //
{
*this += 1;
return *this;
}
Date operator++(int)
{
Date tmp(*this); //拷贝
*this += 1;
return tmp; //拷贝
}
建议用前置++
实现d1--和--d1
Date& operator--() //
{
*this -= 1;
return *this;
}
Date operator++(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
计算两个日期之间相隔天数 d1-d2 2023-5-10 2023-5-1
int operator-(const Date& d) //与日期减天数构成函数重载
{
//默认认为左大右小
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1; //说明间的时候是一个负值
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
const成员
问题:
void TestDate4()
{
const Date d1(2023, 7, 23);
d1.Print(); //报错 d1.Print(&d1) &d1类型是 const Date*类型,实参传递给形参会导致权限放大
就是 d1.Print(&d1);
void Print(Date* this)
void Print() //参数类型是Date*
{
cout << _year << "/" << _month << "/" << _day << endl;
}
}
修改为:
void TestDate4()
{
const Date d1(2023, 7, 23);
d1.Print(); //d1.Print(&d1) &d1类型是 const Date*类型,实参传递给形参会导致权限放大
void Print() const 它就变成void Print(const Date* this)
{
cout << _year << "/" << _month << "/" << _day << endl;
}
}
this不能在实参和形参位置显示写,但是我们可以用