operator重载 —— 操作符篇
基本概念
- 操作符包括了">", “<”, “>=”, “<=”, "== “,”!="等
- 对于内置类型变量可以直接运用操作符,编译器会做处理
- 对于自定义类型,如:日期类,若要使用操作符,往往需要operator函数对操作符重载
operator介绍
基本用法
operator
是一个函数,使用方法是operator
+ 需要重载的操作符
,例如:operator<,这就是一个重载<
操作符的重载函数名。
不能被重载的运算符
- 成员选择运算符:
.
和->
- 作用域解析运算符:
::
- 三元条件运算符:
?:
- 大小运算符:
sizeof
- 类型转换运算符:
typeid
- 范围解析运算符:
.*
和->*
代码示例
日期类>
的操作符重载
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
bool 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;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date a(2024, 5, 28);
Date b(2024, 5, 29);
cout << (a > b) << endl;
return 0;
}
==
操作符重载示例
bool operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
其他操作符重载示例
[!success] 拓展思路
判断小于,可以用大于等于取反
判断小于等于,可以用大于取反
判断不等于,可以用等于取反
// 小于的操作符重载
operator<(const Date& d)
{
return !(*this > d || *this == d);
}
// 小于等于 此处因格式问题,"< ="中间不加空格
operator< =(const Date& d)
{
return !(*this > d);
}
// 大于等于
operator> =(const Date& d)
{
return (*this > d) && (*this == d);
}
输入/输出流函数重载
运用场景
需要用cout << "自定义类型变量" << endl;
的形式输入输出自定义类型。
输入流原理讲解
- 常规思路
使用上面的操作符重载模板来套用。
代码示例
class Date
{
Date(int year, int month, int day)
: _year = year
, _month = month
, _day = day
{}
ostream& operator<<(ostream& out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
return out;
}
};
int main()
{
d1(2024, 5, 29);
cout << d1 << endl; // 此时编译器会报错,不能通过编译
d1 <<cout << endl;
return 0;
}
此时,只能通过d1 << cout << endl;
的形式打印
出现的问题
重载操作符的调用有两种,一种是直接调用函数,例如:d1.operator>(d2);
也可以直接使用操作符,例如:d1 > d2
二者在底层实现上都无区别,但是后者可读性更高,更优雅;
但是需要着重注意的是,第二种调用方式变量的先后位置要与函数声明的先后位置相对应。
也就是说,有下面的函数声明:operator<<(ostream& out);
第一个参数是编译器传入的this
指针,this
指针被规定必须放第一个参数位,第二个才是变量out
因此,第二种形式的调用只能写成d1 << cout << endl;
此处,d1是第一个参数,中间是被重载的运算符,后边的是第二个参数
解决办法
由于this指针只传入类里面的函数。因此,只要把函数声明在类的外面,就可以自定义参数的顺序。
class Date()
{
// ...
};
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
int main()
{
Date d1(2024, 5, 29);
cout << d1 << endl; // 此时会报错,报错的原因是不能够访问类的内部成员
return 0;
}
此时编译器报错,该函数不在类的内部,不能访问类的成员
解决报错
此时使用友元函数就可以访问到类的内部成员函数
友元函数
基本概念
- 友元函数相当于一个声明,声明该函数可以直接访问类的内部成员。
- 友元函数不可被const修饰
class Date
{
// 友元函数声明
friend Date& Date(Date* d1, Date* d2);
};
Date& Date(Date* d1, Date* d2)
{
// ...
}
正确的代码示例
class Date
{
// 友元函数声明
friend std::ostream& operator<<(ostream& out, const Date& d);
};
ostream& std::operator<<(ostream& out, const Date& d)
{
// ...
return out;
}
输出流原理讲解
输出流重载的代码和输入流重载的思路差不多,这里直接代码示例
class Date
{
friend std::istream& operator>>(istream& in, Date& d);
}
std::istream& operator>>(const istream& in, Date& d)
{
cin >> d._year >> d._month >> d._day;
}
注意事项
-
输入/输出流的参数一定要加 引用
&
-
需要返回值以便连续调用,返回值类型是重载的操作符类型,以便链式调用,链式调用是用后一个的返回值再次调用前面的。
cout << d1 << d2 << endl;
-
输入/输出运算符的参数都不能用const来修饰,因为要向流函数增加新的功能
-
输入流的自定义类型不能用const修饰,因为要赋值
cout/cin链式调用详解
- cout和cin的流函数分别是
ostream
和istream
代码实现
int main()
{
std::cout << "Hello" << "World" << std::endl;
return 0;
}
具体实现过程
std::cout << "Hello"
调用operator<<
,用于输出Hello
- 调用结束返回
ostream&
类型的std::cout
的引用 - 此时,
"Hello"
位置的返回值为std::cout
- 因此,现在的情况可以看作是
std::cout << std:: cout << "World" << std::endl;
- 此处的第二个
std::cout
便是之前World
的返回值 - 这个返回值又调用
operator<<
,实现后边内容的输出 - 以此往复,达到链式调用的效果
重点提要
ostream
和istream
类型的参数或返回值必须传引用
原因:ostream
和istream
类型的参数不支持拷贝,且需要修改流的输入和输出,所以必须传引用