1.友元函数
在类的内部可以随意访问私有成员,类的外部不能随意访问私有成员,但总会有时候我们想要从类外面访问私有成员,每次专门为得到这个成员变量而写一个函数过于繁琐,直接用友元函数会更加方便。
友元函数可以
直接访问
类的
私有
成员,它是
定义在类外部
的
普通函数,不属于任何类,但需要在
类的内部声明,声明时需要加friend关键字。
当我们试图重载输入输出操作符时,会发现重载的参数很不符合参数传递的顺序。
因为不管怎么传
cout(cin)的
输出(入)流对象和隐含的this
指针在抢占第一个参数的位置
。
this指针默认是第一个参数也就是左操作
数了。但是实际使用中cout(cin)
需要是第一个形参对象,才能正常使用。所以要将
operator<<和operator>>重载成
全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。
//创建日期类,并声明友元函数
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
//重载后,参数传递顺序符合常规调用
//可能调用多次,故返回值是ostream&(istream&)
ostream&operator<<(ostream&_cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream&operator>>(istream&_cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
因为需要在类外面定义,它不认识类内部成员,这时候就需要friend友元函数。这样一来,我们就完成了自定义类型的输入和输出。
没有友元函数就无法完成自定义类型的输入输出,或者说,不能很好的完成常规调用,友元提供了一种突破封装的方式,有时提供了便利。这既给我们带来了方便,也存在一些弊端:它增加了耦合度,毕竟每一个使用友元的地方都有可能改变原来的私有成员,频繁的使用只会更加破坏封装,所以 友元不宜多用。
2.友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
class A
{
friend class B;
public:
A(int a = 0)
{
_a = a;
}
private:
int _a;
};
class B
{
public:
B(int b = 1)
{
_b = b;
}
void func()
{
aa._a++;
}
private:
int _b;
A aa;
};
B是A的友元,B理应能够访问A内部成员,反之,那么A能否访问B内的成员?
![](https://i-blog.csdnimg.cn/blog_migrate/43e0ce5b412d857cf15abe0138b18028.png)
友元关系是单向的,不具有交换性。
所以得出,在A内声明B是A的友元类,B可以访问A的成员,但A不能访问B的成员。
类似地,友元关系不能传递
如果
C
是
B
的友元,
B
是
A
的友元,则不能说明
C
时
A
的友元。
3.内部类
当一个类中又定义了一个类,
这个内部的类就叫做内部类
。
内部类就是外部类的友元类
,参见友元类的定义,内部类可以通过外部类的对象参数来访
问外部类中的所有成员。
友元关系是单向的,所以只能说明内部类是外部类的友元,而不能得到外部类是内部类的友元。因此,外部类是不能随意访问内部类的成员。
另外,在一个类中定义了内部类以后,计算它的大小时,不会计算内部类,因为内部类的存储其实并没有存在外部类中 。