文章目录
- 赋值运算符重载
- 1.1 运算符重载的引用
- 1.2 运算符重载的概念
- 1.3 赋值运算符重载
- 总结一下(赋值运算符)
赋值运算符重载
1.1 运算符重载的引用
有一个日期类Date:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
}
如上述main函数,有时我们如果想去比较比较两个对象,该如何去比较呢?
首先肯定不能直接比较,因为这不是内置类型,编译器无法直接识别:
根据之前C语言的经验,我们可以利用bool函数来判断,比如我们要去比较两个对象是否相等,可以这样操作:
bool DateEqual(const Date& x, const Date& y)
{
return x._year == y._year
&& x._month == y._month
&& x._day == y._day;
}
int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
cout<<DateEqual(d1,d2)<<endl;
}
当两个日期类对象年月日都不相等时,bool函数就返回false(0);
那么如果是判断两个日期类对象大小关系呢?
我们实现一个函数比较小于:
bool DateLess(const Date& x, Date& y)
{
if (x._year < y._year)
{
return true;
}
else if(x._year == y._year)
{
if (x._month < y._month)
{
return true;
}
else if(x._month == y._month)
{
if (x._day < y._day)
{
return true;
}
}
}
return false;
}
int main()
{
Date d1(2024,1,28);
Date d2(2024,2,27);
cout<<DateLess(d1,d2)<<endl;
这样去写当然不会有问题,不过有个取名字问题的小瑕疵,我们这里函数取名较为正规,其他读者一看基本就知道此函数的作用,可一旦乱取名字,就会变得很混乱。
所以,C++引用了一种新的函数------运算符重载。
1.2 运算符重载的概念
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数基本构成:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
. :: sizeof ?: .*
注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
运用赋值运算符重载我们就可以将上述判断日期类对象大小的函数改为:
bool operator==(const Date& x, Date& y)
{
return x._year == y._year
&& x._month == y._month
&& x._day == y._day;
}
bool operator<(const Date& x, Date& y)
{
if (x._year < y._year)
{
return true;
}
else if(x._year == y._year)
{
if (x._month < y._month)
{
return true;
}
else if(x._month == y._month)
{
if (x._day < y._day)
{
return true;
}
}
}
return false;
}
使用方式我们可以用以下两种:
还可以这样做:
不过第二种方法要注意加上括号,因为流插入的优先级高于 == 和 <
接着往下看,不知道老铁们可注意到,上述我们定义Date类的时候,是把内置类型放开的:
如果设为私有,类外面就无法访问了:
不过此种情况解决方法也很简单,除了将内置类型放开以外,这里更推荐2种方法:
1.第一种就是在类里面搞一个Get函数【Java通常就是这样处理的】
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int GetYear()
{
return _year;
}
private:
int _year;
int _month;
int _day;
};
2.第二种方法就是将函数放到类里面
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& x, Date& y)
{
return x._year == y._year
&& x._month == y._month
&& x._day == y._day;
}
bool operator<(const Date& x, Date& y)
{
if (x._year < y._year)
{
return true;
}
else if (x._year == y._year)
{
if (x._month < y._month)
{
return true;
}
else if (x._month == y._month)
{
if (x._day < y._day)
{
return true;
}
}
}
return false;
}
private:
int _year;
int _month;
int _day;
};
可是这里编译器会报错:参数过多
因为成员函数有一个隐藏的this指针(参照注意点4)
因此我们可以将上述代码修改为:
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(Date& y)
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
bool operator<(Date& y)
{
if (_year < y._year)
{
return true;
}
else if (_year == y._year)
{
if (_month < y._month)
{
return true;
}
else if (_month == y._month)
{
if (_day < y._day)
{
return true;
}
}
}
return false;
}
private:
int _year;
int _month;
int _day;
};
如下使用即可:
1.3 赋值运算符重载
赋值运算符重载也是6大默认成员函数之一(且是最重要的4个成员函数之一),前面几篇博客我们聊到了构造、析构、拷贝构造,从功能上来讲,赋值运算符重载和拷贝构造函数很像。
我们先写一个赋值运算符:
void operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
我们先一起来看看两者有什么区别:
拷贝构造函数,本质是构造函数,它是一个已经存在的对象初始化要创建的同类型对象,就像:
Data d1(2024,1,19);
Data d2(2024,2,17);
Data d3(d1);
而赋值运算符重载是对两个已经存在的对象,一个拷贝赋值给另外一个:
d1=d2;
d1.Print();
d2.Print();
我们可以看到d1被赋值上了d2的值了~
- 但是我们这样写又会遇到新的问题-------无法连续赋值
- 这里d2=d3会去调用赋值运算符,想要连续为d1赋值,就要将d2作为d1=d2的右操作数
- 返回类型用传引用返回(Date&),返回的是
*this
,不要写成this
,因为我们要返回的是对象,this
仅仅是个指针
Date& operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
好了,连续赋值的问题咱们就这样解决了。
- 但是不排除有人会有如下写法:
d1=d1;
其实这样写语法也没有什么问题,但通常我们会在赋值重载里加上一个判断:
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
-
注意:形参里面的&是引用,if判断里面的&为取地址,记得分清楚
-
还有一点就是赋值运算符重载我们不写,编译器也会自动生成一个的
-
内置类型会值拷贝,自定义类型会调用它的拷贝
总结一下(赋值运算符)
- 赋值运算符重载格式
- 参数类型:const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this :要复合连续赋值的含义
- 赋值运算符只能重载成类的成员函数不能重载成全局函数
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;
};
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员
- 注意:赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
预告:下篇博客我们将对默认成员函数收尾,涉及const成员函数及相关周边知识点,且完整实现Date类。
Byebye老铁们,下篇博客再述~