之前我们详细地实现了Date日期类,然后我们这里继续补充一些细节。如果你是不清楚这里的Date类是什么,可以转到这篇文章【C++】类和对象--一篇带你解决运算符重载实例--日期类
在Date日期类中,我们实例化一个普通对象。而普通对象调用普通成员函数需要传参,传参传给隐藏的this指针,下面给出一个例子:
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//这里实例化的是一个普通对象
Date d(2025, 8, 23);
//此时d(普通对象)调用Print
//传参给this指针
//也就是将d1的地址传给了this
d1.Print();
}上面这种情况肯定是能够顺利运行的,但是如果实例化一个const对象呢,还能够顺利编译吗?
int main() {
const Date d2(2025, 8, 23);
d2.Print();
}我们发现报错了:

这是因为将Date实例化为const,那么在调用Print()时,传入的地址类型应该是const Date *,但是this指针是Date的,那么这个转换是不被允许的(因为权限被放大了)。
那么我们第一反应的解决方案肯定就是将这类的this指针类型修改成const Date *,但是我们学过这个this指针是由编译器自动生成的,我们没有办法直接修改。所以C++的设计者这样解决:
//在成员函数参数列表后面加上const修饰
//起作用就相当于void Print(const Date * const this )
//这样一来这个传参过程就能够顺利进行了
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}总结:建议所有不修改成员变量的成员函数都加上const
Q:思考下面几个问题
A:答案
前面讲到,一共有6个默认成员函数,我们已经学习了4个,现在我们来看一看剩下两个(并不是很重要,默认生成的即可):
class Date
{
public:
Date* operator&()
{
return this;
}
const Date* operator&() const
{
return this;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};总结:这两个运算符一般不需要重载,使用编译器默认生成的取地址重载即可。只有在一些特殊情况才需要重载,例如想要让用户看到指定的信息!
Date d2(2025, 8, 23);
cout << d2 << endl;会报错:

显然我们想要完成自定义类的输入和输出,就要进行流提取、流插入的运算符的重载。
但是我们想一下,内置类型都是可以直接使用流提取和流插入运算符的,这似乎好像也是重载哦!
int i = 1;
double d = 2.2;
cout << i;//自动识别类型
cout << d;我们深入了解一下:
cout 和 cin 是全局对象,他们包含在iostream头文件中,所以我们在写C++文件的时候会#include <iostream>。cin是istream的对象,cout是ostream的对象,istream和ostream是库中的流提取和流插入的两个类型。

而<<和>>能够识别不同的内置类型的真正原因就是,把所有内置类型都重载了:

所以其实真实的情况就是:


那么针对Date日期类这种自定义类型,我们也只需要重载一下就可以了。
问题1:
class Date
{
public:
//参数列表中是一个ostream&(是一个别名)来接受传入的对象
void operator<<(std::ostream& out)
{
out << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};但是这样有一个限制,那就是我们只能这两种方式来进行访问:
Date d2(2025, 8, 23);
d2.operator<<(cout);
d2 << cout;
使用我们一般的方式依旧是会报错的:

我们来继续思考:
我们之前讲过成员函数的第一个参数是this指针,放到这里讲的话,意味着重载后<<(双目运算符)左侧必须是当前对象,但是我们使用在输出时的习惯肯定是<<的左侧是cout类型。所以这里我们先想到,运算符重载写在全局上:
void operator<<(std::ostream& out,const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
}全局函数的话,就没有这个限制,第一个参数就可以是ostream类,这样的话就能符合cout正常的使用习惯(cout << d)。
但是,_year、_month、_day是私有的成员变量,所以最后我们通过友元函数(友元函数是一个非成员函数,但它可以访问类的私有(private)和保护(protected)成员。它打破了类的封装性,但提供了必要的灵活性)来解决:
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
//友元函数的声明
friend void operator<<(std::ostream& out, const Date& d);
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//友元函数的定义
void operator<<(std::ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
}运行结果:

问题2:
我们知道对于内置类型,流插入是可以连续访问的:
int i = 1;
double d = 2.2;
cout << i << "-" << d << endl;但是通过我们上面的方法似乎不太行,依旧是会包没有匹配的参数列表。举一个例子:
Date d1(2025, 8, 21);
Date d2(2025, 8, 23);
cout << d1 << d2;这里会先调用 cout<<d1 ,然后会返回一个返回值,这个返回值会作为下一次流插入的左参数。但是流插入的左参数同样也应该是一个cout(ostream)类型的,因此我们这里应该有一个返回值。
最终正确版本:
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
friend ostream& operator<<(std::ostream& out, const Date& d);
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//友元函数
//注意我这里的代码是从cpp文件中截取出来的
//是包含了头文件的
//如果没有包含头文件
//需要 std::
ostream& operator<<(std::ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << 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);
//!!!
//Date要赋值修改 所以不能const
public:
private:
int _year;
int _month;
int _day;
};
std::ostream& operator<<(std::ostream& out,const Date&d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
std::istream& operator>>(std::istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}#include <iostream>
using namespace std;
class Date
{
public:
Date(int year=0, int month=0, int day=0)
{
_year = year;
_month = month;
_day = day;
}
//流插入
friend ostream& operator<<(ostream& out, const Date& d);
//流提取
friend istream& operator>>(istream& in, Date& d);
//注意这里的Date不能是const,因为要被修改
private:
int _year;
int _month;
int _day;
};
//友元函数
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
int main() {
Date d1, d2;
cin >> d1;
cout << d1;
cin >> d2;
cout << d2;
return 0;
}
到这里我们的Date日期类就比较完整了,大家可以自己练习练习。
(本篇完)