目录
解释:成员函数后面加const以后普通对象和const对象都可以调用
承接CD21.【C++ Dev】类和对象(12) 流插入运算符的重载文章
1.流提取运算符>>的重载
知识回顾
流提取运算符的基础知识参见CC2.【C++ Cont】初认识cout,cin和endl文章
重载方法
和CD21.【C++ Dev】类和对象(12) 流插入运算符的重载文章的operator<<重载格式一样
operator<<格式
ostream& operator<<(ostream& out, const Date& d)
operator>>格式
(来自https://legacy.cplusplus.com/reference/istream/istream/operator%3E%3E/)
查资料可知,返回值应该为istream&,第一个参数的也为istream&
istream& operator>>(istream& in, const Date& d)
{
//......
return in;
}
注意:两个参数都不能使用const修饰
1.cin是一个全局对象,输入实际上是通过的cin这个对象,输入的东西需要cin去接受,如果加了const,cin就无法修改
2.由于需要对d的成员变量写入数据,不能加const
在类里面写operator>>友元函数声明,便于访问私有成员变量:
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
//......
private:
int _year;
int _month;
int _day;
};
使用cin对日期类对象写入数据
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
测试代码:
#include "Date.h"
int main()
{
Date d1(0, 0, 0);
cin >> d1;
cout << d1;
return 0;
}
运行结果:
如果想指定格式输入
方法1:getchar()
例如输入2025/3/31.可以这样修改代码
istream& operator>>(istream& in, Date& d)
{
in >> d._year;
getchar();
in >> d._month;
getchar();
in>> d._day;
return in;
}
运行结果:
方法2:使用临时变量接收字符
该临时变量仅接收字符,并无实际作用,所以起名为unused
istream& operator>>(istream& in, Date& d)
{
char unused;
in >> d._year >> unused >> d._month >> unused >> d._day;
return in;
}
运行结果:
完善operator>>代码(修bug)
代码要有鲁棒性,因此需要对非法日期做处理
1.月必须介于1~12之间
2.天数必须合法
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
if (d._month < 1 || d._month>12)
{
cout << "非法日期" << endl;
exit(EXIT_FAILURE);//错误退出
}
if (d._day <= 0 || d._day > GetMonthDay(d._year,d._day))
{
cout << "非法日期" << endl;
exit(EXIT_FAILURE);//错误退出
}
return in;
}
(也可以将exit(EXIT_FAILURE)改成assert(false),因为里面为false,为无条件断言)
其他写法:将判断条件写入assert中:
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
assert(!(d._month < 1 || d._month>12));
assert(!(d._day <= 0 || d._day > GetMonthDay(d._year, d._month)));
return in;
}
这样断言的好处:能具体告诉日期的哪一个部分是非法的,例如:
1.月份是非法的
2.天数是非法的
2025年不是闰年,2月最多28天
运行结果:
发现退出代码不为0,这和头文件中EXIT_FAILURE定义的值有关
(VS2022的stdlib.h中有定义)
2.类中的权限问题(const成员)
使用const绕不开权限的放大缩小平移的问题,看下面的例子:
Date中的Print函数:
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
测试代码:
#include "Date.h"
int main()
{
const Date d1(2025, 3, 31);
d1.Print();
return 0;
}
报错:
分析:
Print()中隐藏的参数是this,虽然this指针的类型为const Date*,但是 const Date*中const只表示this指针本身不能修改,并没有表示this指向对象的成员变量不能修改
实参this表示this指向的成员变量不可以修改(const Date d1(2025, 3, 31);),但Print()接收的this表示this指向的成员变量可以修改(void Date::Print()),导致权限的放大
解决方法:在Print函数后面加上const即可,const修饰*this(注意星号),这样就为权限的平移
void Date::Print() const//const修饰*this,this的类型为const Date* const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
(注加上的const不能写在Print()的括号里面,隐式参数this类型不能在括号里面修改,这是语法规定)
运行结果:
问题
下面是权限的平移放大还是缩小?是否正常运行?
void Date::Print() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//......
#include "Date.h"
int main()
{
Date d1(2025, 3, 31);
d1.Print();
return 0;
}
分析
Date d1(2025, 3, 31);表示this指向的对象可以修改,void Date::Print() const表示this指向的对象不可以修改,从可以修改到不可以修改为权限的缩小,正常运行
建议
★如果对象的成员变量不改变(注意前提),最好在成员函数的后面加上const,成员函数后面加const以后普通对象和const对象都可以调用
例如日期+天数,日期类对象不改变,则在operator+函数后加上const
提醒:非成员函数上不允许修饰符const
(operator<<不是成员函数,没有使用Date::)
但也不能不看实际情况就加const
例如operator-复用了operator-=的代码,加了const就不能对*this修改
解释:成员函数后面加const以后普通对象和const对象都可以调用
测试代码:
#include "Date.h"
int main()
{
const Date d1(2025, 3, 31);
Date d2(2025, 4, 1);
d1 < d2;
d2 < d1;
return 0;
}
分别对加了const和没有加const的operator<测试
先分析没有加const的:
bool Date::operator< (const Date& d2)
{
if (_year < d2._year)
return true;
else if (_year == d2._year && _month < d2._month)
return true;
else if (_year == d2._year && _month == d2._month && _day < d2._day)
return true;
return false;
}
读测试代码可知:
d1<d2的执行会有问题,由d1的定义可知:d1的成员对象不能修改,而bool Date::operator< (const Date& d2)第一个参数为this,与d1的地址对应,导致权限的放大,会报错
d2<d1执行没有问题,是权限的缩小,没有问题
再分析加了const的:
d1<d2:权限的平移,没有问题
d2<d1:权限的缩小,没有问题
3.const练习
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?
答案见下篇