【C++】-- 友元

简介: 【C++】-- 友元

一、为什么要使用友元

对于自定义类型Date类,为了打印Date类的对象,需要我们自己在Date类中写打印函数:

1. void Print() const
2. {
3.  cout << _year << "-" << _month << "-" << _day << endl;
4. }

而内置类型在不写打印重载函数的情况下却能够直接打印:

1. #include<iostream>
2. using namespace std;
3. 
4. int main()
5. {
6.  int a = 0;
7.  cout << a << endl;
8.  return 0;
9. }

这是因为库里面对内置类型写了许多运算符重载函数,包括operator>>和operator<<并且能够自动识别类型构成重载,所以内置类型直接能调打印。而自定义类型需要自己写打印函数。

假如库里面对内置类型没有写operator>>和operator<<重载,对于自定义的Date类那就只有在类中自己写operator>>和operator<<重载函数了:

1. #include<iostream>
2. using namespace std;
3. 
4. class Date
5. {
6. public:
7.  //构造函数
8.  Date(int year = 2022, int month = 4, int day = 8)
9.  {
10.     _year = year;
11.     _month = month;
12.     _day = day;
13.   }
14.     
15.     //void Date::Print() const
16.     //{
17.     //  cout << _year << "-" << _month << "-" << _day << endl;
18.     //}
19.     
20.   void operator<<(ostream& out)
21.   {
22.     out << _year << "-" << _month << "-" << _day << endl;
23.   }
24. 
25.   void operator>>(istream& in)
26.   {
27.     in>>_year;
28.     in >> _month;
29.     in >> _day;
30.   }
31. 
32. private:
33.   int _year;
34.   int _month;
35.   int _day;
36. };
37. 
38. int main()
39. {
40.   Date d1;
41.   cout << d1;
42. 
43.   return 0;
44. }

但是用cout<<d1却调用不了<<运算符:

我们需要的是cout<<d1,但是调用operator<<实际上执行的是:

void operator<<(ostream& out) //void operator<<(Date* this, ostream& out)

d1作为this就变成了第一个参数,main函数里面的调用应该是d1<<cout,编译通过:

1. int main()
2. {
3.  Date d1;
4.  d1 << cout;//我们需要cout<<d1
5. 
6.  return 0;
7. }

但是d1<<cout的调用不符合我们的习惯,可读性不高,我们需要cout作为第一个参数,这样就会造成d1和cout抢占第一个参数的问题。假如把operator>>和operator<<重载不写在类里面,而是写成全局函数写在Date类外面,我们可以指定参数顺序:

1. #include<iostream>
2. using namespace std;
3. 
4. class Date
5. {
6. public:
7.  //构造函数
8.  Date(int year = 2022, int month = 4, int day = 8)
9.  {
10.     _year = year;
11.     _month = month;
12.     _day = day;
13.   }
14. 
15. private:
16.   int _year;
17.   int _month;
18.   int _day;
19. };
20. 
21. void operator<<(ostream& out,const Date& d)
22. {
23.   out << _year << "-" << _month << "-" << _day << endl;
24. }
25. 
26. void operator>>(istream& in, const Date& d)
27. {
28.   in >> _year;
29.   in >> _month;
30.   in >> _day;
31. }
32. 
33. int main()
34. {
35.   Date d1;
36.   cout << d1;
37. 
38.   return 0;
39. }

但会带来访问不了私有成员变量的问题:

如何能够访问到类的私有成员变量,突破类域呢?-------使用友元,友元提供了一种突破封装的方式。友元分为友元函数和友元类。


二、友元函数

1.友元函数定义

友元函数是定义在类外部的普通函数,不属于任何类,但可以直接访问类的私有成员,需要在类的内部使用friend关键字进行声明,类就会把友元函数当作类里面的成员。

1. #include<iostream>
2. using namespace std;
3. 
4. class Date
5. {
6.     //友元函数
7.     friend ostream& operator<<(ostream& out, const Date& d);
8.     friend istream& operator>>(istream& in, Date& d);
9. 
10. public:
11.   //构造函数
12.   Date(int year = 2022, int month = 4, int day = 8)
13.   {
14.     _year = year;
15.     _month = month;
16.     _day = day;
17.   }
18. 
19. private:
20.   int _year;
21.   int _month;
22.   int _day;
23. };
24. 
25. ostream& operator<<(ostream& out,const Date& d)
26. {
27.   out << d._year << "-" << d._month << "-" << d._day << endl;
28. 
29.   return _cout;
30. }
31. 
32. istream& operator>>(istream& in, Date& d)
33. {
34.   in >> d._year;
35.   in >> d._month;
36.   in >> d._day;
37. 
38.   return in;
39. }
40. 
41. int main()
42. {
43.   Date d1;
44.   cin >> d1;
45.   cout << d1 <<endl;
46. 
47.   return 0;
48. }

2.友元函数特性

(1)友元函数可访问类的私有和保护成员,但不是类的成员函数

(2)友元函数不能用const修饰(const修饰this指针指向的内容,友元函数作为全局函数没有this指针)

(3)友元函数可以在类定义的任何地方声明,不受类访问限定符限制

(4)一个函数可以是多个类的友元函数

(5)友元函数的调用与普通函数的调用和原理相同


三、友元类

1.友元类定义

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。如下Date类是Time类的友元类,Date类要访问Time类,就要把Date类定义成Time类的友元:

1. class Date; // 前置声明
2. class Time
3. {
4.  friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
5. public:
6.  Time(int hour = 0, int minute = 0, int second = 0)
7.    : _hour(hour)
8.    , _minute(minute)
9.    , _second(second)
10.   {}
11. 
12. private:
13.   int _hour;
14.   int _minute;
15.   int _second;
16. };
17. 
18. class Date
19. {
20. public:
21.   Date(int year = 1900, int month = 1, int day = 1)
22.     : _year(year)
23.     , _month(month)
24.     , _day(day)
25.   {}
26.   void SetTimeOfDate(int hour, int minute, int second)
27.   {
28.     // 直接访问时间类私有的成员变量
29.     _t._hour = hour;
30.     _t._minute = minute;
31.     _t._second = second;
32.   }
33. 
34. private:
35.   int _year;
36.   int _month;
37.   int _day;
38.   Time _t;
39. };
40. 
41. int main()
42. {
43.   return 0;
44. }

2.友元类特性

(1)友元关系是单向的,不具有交换性。

在Time类中,声明Date 类是其友元类,可以在Date类中直接访问Time类的私有成员。但不能在Time中访问Date类中的私有成员。

(2)友元关系不能传递

A是B的友元,B是C的友元,但A不是C的友元。


四、内部类

1.内部类定义

一个类定义在另一个类的内部,这个内部类就叫做内部类。

内部类和外部类关系:

(1)内部类不属于外部类,更不能通过外部类的对象调用内部类。外部类对内部类没有任何优越的访问权限。

(2)内部类是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类的所有成员,但外部类不是内部类的友元。

1. #include<iostream>
2. using namespace std;
3. 
4. class A {
5. private:
6.  static int k;
7.  int h;
8. public:
9.  class B  //B是A的内部类,B是A的友元类
10.   {
11.   public:
12.     void foo(const A& a)
13.     {
14.       cout << k << endl;//内部类可以访问外部类的非公有成员
15.       cout << a.h << endl;//普通成员只能通过对象访问,不能通过类名访问
16.     }
17. 
18.   private:
19.     int _b;
20.   };
21. };
22. 
23. int A::k = 1;
24. 
25. int main()
26. {
27.   A::B b; //定义一个内部类对象
28.   b.foo(A());
29. 
30.   return 0;
31. }

2.内部类特性

(1)内部类可以定义在外部类的public、protected、private都是可以的。

(2)注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。

(3)sizeof(外部类)=外部类,和内部类没有任何关系

1. int main()
2. {
3.  A::B b;
4.  b.foo(A());
5.     
6.     //由于静态成员变量不在对象中存储,类的大小为非静态成员变量内存对齐的结果,A只有一个非静态成员变量
7.  cout << "sizeof(A) = " << sizeof(A) << endl;
8. 
9.  return 0;
10. }


五、总结

A无论是类还是全局函数,只要A被定义为B的友元,A就可以访问B的非公有成员。


相关文章
|
5月前
|
程序员 C++
29 C++ - 友元
29 C++ - 友元
27 0
|
1月前
|
安全 编译器 程序员
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用(一)
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用
31 1
|
3月前
|
存储 算法 C语言
【C++入门到精通】C++入门 —— 类和对象(初始化列表、Static成员、友元、内部类、匿名对象)
一、初始化列表 ⭕初始化列表概念 ⭕初始化列表的优点 ⭕使用场景 ⭕explicit关键字 二、Static成员 ⭕Static成员概念 🔴静态数据成员: 🔴静态函数成员: ⭕使用静态成员的
69 0
|
4天前
|
编译器 C++
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
|
11天前
|
C++
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
|
1月前
|
自然语言处理 安全 程序员
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用(二)
【C++ 泛型编程 进阶篇】 C++ 泛型编程 模板与异常处理、模板与友元之间的使用
26 0
|
2月前
|
C++
【c++】友元
【c++】友元
【c++】友元
|
2月前
|
C++
C++类与对象【友元】
C++类与对象【友元】
|
3月前
|
C++
c++类与对象(五):友元、内部类、临时对象、匿名对象
c++类与对象(五):友元、内部类、临时对象、匿名对象
22 0
|
24天前
|
C++
[C++/PTA] 友元很简单2016final
[C++/PTA] 友元很简单2016final
26 0