接着日期类
void testDate6()
{
Date d1;
cin >> d1;
//>>是流提取,意为从流中提取,放到d1中。
cout << d1;
//<<是流插入,意为把对象插入到流中。
}
这两个运算符都是需要重载的。
具体如何重载需要引入一个概念。
已知:内置类型可以直接使用运算符,cout会自动识别内置类型。
cout如何自动识别类型的?cout和cin具体又是什么?
先从下面这张图开始说明
cin和cout其实是全局的对象,包含在头文件iostream中
cin是istream类型的对象,cout是ostream类型的对象,ostream类型的对象还有cerr,clog。istream和ostream是流的类型
cout能输出各种类型就是因为cout把各种类型都重载了
cin >> a;
cout << a;
变量a通过运算符重载找到对应的重载函数,返回一个ostream类型,由cout << a;可知,打印是通过cout对象实现的,所以写日期类<<运算符的重载需要ostream类型的变量;>>运算符的重载需要istream类型的变量。
void operator<<(std :: ostream& out)
//ostream在std中,没有展开std的话要声明所在域
{
out << _year << "-" << _mouth << "-" << _day << endl;
}
不过这样写cout << d1;会报错。因为位置不对。
因为cout << a;在调用是会转换成cout.operator<<(a);而cout << d1;会转换成cout.operator<<(d1);d1和out类型不一致,没有找到匹配的重载函数,报错。想要调用应该是d1.operator<<(cout);运算符<<重载是写在日期类中的,应该是日期类来调用。
不写成重载形式的调用方式是d1 << cout;
之前规定过,当一个运算符是双操作数时,第一个数是左操作数,第二个数是右操作数。但是流插入运算符的语法是日期类插入到流中去,重载变成了流插入到日期类对象中。
函数调用只能像d1<<cout;这样调用的主要原因就是写成了日期类的成员函数。成员函数有一个规定死的:第一个参数必是隐含的this。
所以函数应该写在全局变量域中
void operator<<(std :: ostream& out, const Date& d)
{
out << d._year << "-" << d._mouth << "-" << d._day << endl;
}
现在的问题就是_year这些成员变量是私有的。解决方案有两个。
1.写GetYear函数,GetYear函数如果是引用返回,引用接收,就和公开成员变量没什么区别了,可以对私有成员进行修改。
2.友元
class Date
{
friend void operator<<(std :: ostream& out, const Date& d);
//友元就是在前面加一个friend声明,同时声明operator<<函数
pubilc:
……
}
友元函数可以访问类的私有成员变量。
这里还有一个隐含的问题,class Date是定义在Date.h 中的,所以void operator<<(std :: ostream& out, const Date& d)函数往往会顺手写在Date.h中,而void operator<<(std :: ostream& out, const Date& d)又是全局变量函数,在test.cpp和Date.cpp中都有Date.h的展开,也就是说void operator<<(std :: ostream& out, const Date& d)函数被定义了两次,在符号表中存在两个相同的函数标识符,会报重定义的错。
类中的函数和全局变量函数都在静态区,为什么类中的函数不报错?
类中的函数默认是内联函数,不会放入符号表。即使类中定义的函数很长,不具备内联函数的条件,编译器不会展开这个函数,但是这个函数依然有内联函数的属性。声明了是内联后,编译器决定要不要展开。不展开也还是具备内联的属性。
问题解决了,但是和<<操作符的实际作用还存在区别,因为不支持连续流插入。
<<操作符是从左到右结合,cout << d1 << d2;先走cout << d1,再根据cout << d1的返回值执行 << d2。
class Date
{
friend std :: ostream& operator<<(std :: ostream& out, const Date& d);
pubilc:
……
}
std :: ostream& operator<<(std :: ostream& out, const Date& d)
{
out << d._year << "-" << d._mouth << "-" << d._day << endl;
return out;
}
这样是为了连续的流插入。
流提取:
class Date
{
friend std :: ostream& operator<<(std :: ostream& out, const Date& d);
friend std :: istream& operator>>(std :: istream& in, Date& d);
pubilc:
……
}
std :: ostream& operator<<(std :: ostream& out, const Date& d)
{
out << d._year << "-" << d._mouth << "-" << d._day << endl;
return out;
}
std :: istream& operator>>(std :: istream& in, Date& d)
{
in >> d._year >> d._mouth >> d._day;
//这里输入非法日期是不会报错的,所以应该再加上一个检查
return in;
}
流本质上是一个类型(istream/ostream)的对象,这个对象的作用是输入/输出。在这个对象中的数据都会输出到屏幕上/存储进内存中的变量里。是由系统控制的。
到这里,日期类才算有一个完整的框架&