
本篇引入:
之前我们详细地实现了Date日期类,然后我们这里继续补充一些细节。如果你是不清楚这里的Date类是什么,可以转到这篇文章【C++】类和对象--一篇带你解决运算符重载实例--日期类
目录
1. const成员
1.1 const修饰类的成员函数
在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
- const -> const 权限大小不变
- 普通 -> const 权限变小(是编译器允许的)
Q:思考下面几个问题
- const对象可以调用非const成员函数吗?
- 非const对象可以调用const成员函数吗?
- const成员函数内可以调用其它的非const成员函数吗?
- 非const成员函数内可以调用其它的const成员函数吗?
A:答案
- 不能,权限被放大了
- 能,权限被缩小了
- 不能,权限被放大了
- 能,权限被缩小了
2. 取地址及const取地址操作符重载
前面讲到,一共有6个默认成员函数,我们已经学习了4个,现在我们来看一看剩下两个(并不是很重要,默认生成的即可):
class Date { public: Date* operator&() { return this; } const Date* operator&() const { return this; } private: int _year; // 年 int _month; // 月 int _day; // 日 };总结:这两个运算符一般不需要重载,使用编译器默认生成的取地址重载即可。只有在一些特殊情况才需要重载,例如想要让用户看到指定的信息!
3. 流提取和流插入运算符重载
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日期类这种自定义类型,我们也只需要重载一下就可以了。
3.1 流插入运算符重载
问题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; }
3.2 流提取运算符重载
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;
}
3.3 测试:
#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日期类就比较完整了,大家可以自己练习练习。
(本篇完)










1万+

被折叠的 条评论
为什么被折叠?



