上文回顾:我们已经学会了构造函数,析构函数,和拷贝构造函数,接下来我们将学习赋值运算符重载。
class Date
{
public:
Date(int year = 1, int month = 1,int day=1)
{
_year = year;
_month = month;
_day = day;
}
//运算符重载
Date& operator=(const Date& d)
{
_year = d._year;
_day = d._day;
_month = d._month;
return *this;
}
private:
int _year;
int _month;
int _day;
};
如何使用运算符重载?
int main()
{
Date d1(2023,4,10);
Date d2(2023,4,10);
d2=d1;//d2已经定义过了,不是拷贝构造
return 0;
}
//d2的值和d1相同
运算符重载函数可以使已有的两个实例,进行拷贝复制,d2的值被修改后和d1相同。
运算符重载是对已有的两个实例,而拷贝构造函数是在一个实例初始化时对它进行拷贝构造
Date d2(d1)//拷贝构造,借助d1来实现初始化
拷贝构造常用于传参和返回值时,c++规定,传值传参和返回值时,内置类型是直接拷贝,自定义类型先调用拷贝构造,要避免自定义类型调用拷贝构造,1.用指针(指针是内置类型),2.用引用传参和作返回值。
赋值运算符重载
1.不改变被复制的对象,参数用cosnt T&
2.返回值是*this,被赋值对象,应为d2的生命周期不在重载函数内,可以用引用作返回值类型提高效率。
d3 = d2 = d1//先d1覆盖d2再赋值给d3,所以d2=d1的返回值应是d2,被赋值的对象
3.要检测是否给自己赋值。
运算符重载
上面提到的赋值运算符重载,以及接下来说的都属于运算符重载,区别在于赋值运算符重载是类对象的默认函数,即会自动生成,当然涉及到深拷贝时要自己写。其他运算符重载则不是默认函数。
运算符重载本质上是一种函数,它编译器以我们的希望的方式使用运算符,对自定义对象进行操作。下面是对<小于号的运算符重载,按我们的想法实现对日期对象进行比较。(先年后月再日)
下面是两种定义方式:
//在类里面声明,在其他文件中定义
bool Date::operator<(const Date& x)
{
if (_year < x._year)
{
return true;
}
else if (_year == x._year && _month < x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _day < x._day)
{
return true;
}
return false;
}
//在类外声明
bool operator<(Date d,const Date& x)
{
if (d._year < x._year)
{
return true;
}
else if (d._year == x._year && d._month < x._month)
{
return true;
}
else if (d._year == x._year &&d._month == x._month && d._day < x._day)
{
return true;
}
return false;
}
//调用
d1>d2;
运算重载注意的地方:
1.不能连接其他符号进行重载 如operator@
2.重载运算符必须要有个类对象类型,即不能对全是内置类型进行重载操作,有一个自定义类型就能满足目的。
3.作为类对象内置类型,参数会少一个,隐藏了默认this指针。
4. .* ?: :: . sizeof 这五个运算符不能重载。
5. 运算符重载函数的参数顺序,决定运算符使用时的顺序。
bool Date::operator==( Date& d)
{
return _year==d._year&&_month==d._month&&_day==d._day;
}
结合== 运算符和上面的<运算符可以套用其他运算符。
如>运算符重载。
bool operator>(Date& d)
{
return !(*this
+= 运算符原来的数据改变了,+ 原数据没变
a+=1;//a改变了
b=a+1//a没变
因此+=可以返回类的引用,而+必须拷贝构造一个对象,对像加减再返回,返回的是一个类。
Date& Date::operator+=( int day)
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day> GetMonthDay(_year, _month))
{
_day = _day - GetMonthDay(_year, _month);
_month = _month + 1;
if (_month > 12)
{
_month = 1;
_year++;
}
}
return *this;
}
Date Date::operator+(const int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
+套用+=的代码,调用拷贝构造的次数更少。因为+肯定会调用两次拷贝构造,第一次是创建一个新的类时,第二次是返回类对象时,返回自定义对象,会先拷贝构造(上面引用中提到)。+套用+=代码,两个函数各调用一次只会调用两次拷贝构造,+=套用+代码时,各调用一次,会调用四次拷贝构造。-=/-同理。
前置++和后置++运算符重载
前置++,返回的是++后的结果,可以直接直接返回对象引用。后置++,返回的是处理前的对象,并且对象+1后的结果不返回。
首先可以把++转换为+=1,后置++可以拷贝构造一个对象返回新对象,并对原对象++,因为返回的对象是在函数内创建的,出了函数就会销毁,所以只能返回对象。
C++中为了区分前置++和后置++,让后置++重载函数接收一个int参数。
//前置加加
Date& Date::operator++()
{
*this += 1;
return *this;
}
//后置加加
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
对<<,>>操作符进行重载
cout<<1<<2<<3<>d;//流提取
流插入/提取的顺序会赋值运算符不同,是从左往右的,
先cout<<1,然后返回cout再cout<<2。因此流提取的返回值是cout对象,cin同理。
下面代码中的out 就相当cout,是ostream 的类对象。
ostream& operator<<(ostream& out, Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
int year, month, day;
in >> year >> month >> day;
if (year >= 0 && month >= 1 && month <= 12 && day > 0 && day <= d.GetMonthDay(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "日期非法" << endl;
assert(false);
}
return in;
}
注意上面这两个函数是全局函数。由此产生两个问题。
1.为什么是全局函数?
2.如何访问private中的成员变量?
为什么是全局函数?
ostream& Date::operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << endl;
return out;
}
//调用
d1<
如果写在类里面,只能以上面的调用的方式来使用。因为在类函数中,this默认最左边的参数值,而运算符重载又是对参数顺序有要求,要符号我们常规的格式cout< 那么如何访问私有成员变量呢? 在类中使用friend 把它定义为友元函数,实现全局函数对私有成员变量的访问。放在公有私有中都没问题。 如参数里的const Date& d,是防止参数对象的变量被修改,而后面的const 是用来修饰隐式的this指针,防止this 指针指向的对象 被修改。 实际上相当于 那么使用const 最好使用,因为可以保证普通对象和const 修饰的对象都能使用。 在d1 如果修改为bool Date::operator>(const Date& d) const; 则都能运行。class Date
{
friend ostream& operator<<(ostream& out, Date& d);
friend istream& operator>>(istream& in, Date& d);
......
}
const修饰this指针,允许普通对象和const修饰的对象都能调用。
//大于运算符重载
bool Date::operator>(const Date& d)const
{
if (_year >= d._year)
{
if (_month >= d._month)
{
if (_day > d._day)
{
return true;
}
}
}
return false;
}
bool operator>(const Date& d1 ,const Date& d);
bool Date::operator>(const Date& d);
.......
const Date d1;
Date d2;
d2>d1 //可以运行
d1