「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」
❓ 重载<< ❔
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);//友元,位置可任意,一般是开头
friend istream& operator>>(istream& in, Date& d);//友元
public:
Date(int year = 0, int month = 0, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
/*void operator<<(ostream& out)
{
out << _year << "/" << _month << "/" << _day << endl;
}*/
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 >> d2;
//cout << d1 ? d1 << cout
//cout << d1;
/*d1.operator<<(cout);
d1 << cout;*/
cout << d1 << d2;
return 0;
}
📝说明
cin | cout 怎么接收 ❓
在 C++ 里 cout 是一个 ostream 的对象;cin 是一个 istream 的对象。
cout << d1 | d1 << cout(d1.operator<<(cout)) ❓
为啥不能 cout << d1 呢 ?
之前说过运算符有几个操作数,重载函数就有几个参数。如果有两个操作数,左操作数是第一个参数,右操作数是第二个参数。
在 Date 类成员函数 operator<< 里对象是第一个参数,因为隐含的 this 指针已经默认占据了,那么 cout 就只能作第二个操作数了。可以倒也可以,但是用起来不符合流运算符原来的特性。
怎么 cout << d1 呢 ?
也就是把 cout 作为第一个参数,那么这里就不能用成员函数了,之前我们用成员函数是因为成员变量是私有的。
如何取舍:使用成员函数 | 使用全局函数。这里成员函数的可读性差影响较大,所以将之舍弃,使用全局函数。
支持 cout << d1 后怎么解决私有 ?
解决方案1:提供公有的成员函数 GetYear、GetMonth、GetDay
解决方案2:友元函数
这里我们就引出了友元,C++ 默认是不能在类外访问私有的,但是它提供了友元以帮助我们解决这种场景的问题。友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加 friend 关键字。
cout << d1 << d2; ❓
注意与连续赋值大相径同,只是方向相反。
💨小结
1、友元函数可访问类的私有和保护成员,但不是类的成员函数。
2、友元函数不能用 const 修饰,因为 const 修饰的是 this 指针指向的对象。
3、友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
4、一个函数可以是多个类的友元函数。
5、友元函数的调用与普通函数的调用和原理相同。
注意以上部分概念需要与后面的知识结合 ,不懂的可先忽略。
🍳拓展
这里主要拓展代码错误的解决能力,先来看一段代码。
class A
{
friend void f(const A& aa, const B& bb);
public:
A(int a = 0)
: _a(0)
{}
private:
int _a;
};
class B
{
friend void f(const A& aa, const B& bb);
private:
int _b = 0;
A _aa;
};
void f(const A& aa, const B& bb)
{
cout << aa._a << endl;
cout << bb._b << endl;
}
int main()
{
A aa;
B bb;
f(aa, bb);
return 0;
}
📝分析
相信到了这里绝大部分的人都能凭借着自己的经验去解决大部分的 bug了。但是对于上面代码出现的错误又百思不得其解。
注意面对这种情况的时候,有时候编译器报的错误是不准确的,这里有两条建议能帮助提升查找 bug 的能力。
1、有很多错误的时候,一定是看最上面的错误,因为下面的错误有可能是上面的错误间接导致的。
2、排除法,这里有一百行代码(程序崩了),你注释掉了部分代码程序正常了,那么不用怀疑,错误就是注释处代码引发的。
经过分析,我们发现错误处是:在 B 类的友元里能找到 A、B;但是在 A 类的友元里就找不到 B 了。所以解决方法就是加前置声明 —— class B;