设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间21 现在时间21 缓存数据 你是我生命中所能经历的,最最深切的感觉。

你是我生命中所能经历的,最最深切的感觉。

查看: 828|回复: 1

C++中友元类和嵌套类使用详解

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:230
  • 打卡月天数:0
  • 打卡总奖励:3470
  • 最近打卡:2025-09-14 01:35:35
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
445
主题
395
精华
0
金钱
4754
积分
899
注册时间
2023-1-4
最后登录
2025-9-14

发表于 2023-2-13 10:43:08 | 显示全部楼层 |阅读模式
前言

友元这个词,在学习类的时候肯定接触过,但是当时我们只用了很多友元函数。
友元有三种:

  • 友元函数
  • 友元类
  • 友元类方法
类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所以方法都能访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。

1. 友元类

假如我们有两个类:
  1. Tv
复制代码
电视机类,
  1. Remote
复制代码
遥控器类。那么这两个类是什么关系呢?既不是has-a关系,也不是 is-a关系,但是我们知道遥控器可以控制电视机,那么遥控器必须能够访问电视机的私有或保护数据。所以,遥控器就是电视机的友元。
类似于友元函数的声明,友元类的声明:
  1. friend class Remote;
复制代码
友元声明可以位于公有、私有或保护部分,其所在的位置无关紧要。
下面是代码实现:
  1. //友元类1.h
  2. #ifndef TV_H_
  3. #define TV_H_
  4. class Tv
  5. {
  6. private:
  7.     int state;//On or Off
  8.     int volume;//音量
  9.     int maxchannel;//频道数
  10.     int channel;//频道
  11.     int mode;//有线还是天线,Antenna or Cable
  12.     int input;//TV or DVD
  13. public:
  14.     friend class Remote;
  15.     enum{Off,On};
  16.     enum{MinVal,MaxVal=20};
  17.     enum{Antenna,Cable};
  18.     enum{TV,DVD};
  19.     Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc),
  20.                     channel(2),mode(Cable),input(TV){}
  21.     void onoff(){state=(state==On)?Off:On;}
  22.     bool ison() const{return state==On;}
  23.     bool volup();
  24.     bool voldown();
  25.     void chanup();
  26.     void chandown();
  27.     void set_mode(){mode=(mode==Antenna)?Cable:Antenna;}
  28.     void set_input(){input=(input==TV)?DVD:TV;}
  29.     void settings() const;//display all settings
  30. };
  31. class Remote
  32. {
  33. private:
  34.     int mode;//控制TV or DVD
  35. public:
  36.     Remote(int m=Tv::TV):mode(m){};
  37.     bool volup(Tv & t){return t.volup();}
  38.     bool voldown(Tv & t){return t.voldown();}
  39.     void onoff(Tv &t){t.onoff();}
  40.     void chanup(Tv &t){t.chanup();}
  41.     void chandown(Tv &t){t.chandown();}
  42.     void set_chan(Tv &t,int c){t.channel=c;}
  43.     void set_mode(Tv &t){t.set_mode();}
  44.     void set_input(Tv &t){t.set_input();}
  45. };
  46. #endif
复制代码
  1. //友元类1.cpp
  2. #include"友元类1.h"
  3. #include<iostream>
  4. bool Tv::volup()
  5. {
  6.     if(volume<MaxVal)
  7.     {
  8.         volume++;
  9.         return true;
  10.     }
  11.     else
  12.         return false;
  13. }
  14. bool Tv::voldown()
  15. {
  16.     if(volume>MinVal)
  17.     {
  18.         volume--;
  19.         return true;
  20.     }
  21.     else
  22.         return false;
  23. }
  24. void Tv::chanup()
  25. {
  26.     if(channel<maxchannel)
  27.         channel++;
  28.     else
  29.         channel=1;
  30. }
  31. void Tv::chandown()
  32. {
  33.     if(channel>1)
  34.         channel--;
  35.     else
  36.         channel=maxchannel;
  37. }
  38. void Tv::settings() const
  39. {
  40.     using std::cout;
  41.     using std::endl;
  42.     cout<<"Tv is "<<(state==Off? "Off":"On")<<endl;
  43.     if(state==On)
  44.     {
  45.         cout<<"Volume setting = "<<volume<<endl;
  46.         cout<<"Channel setting = "<<channel<<endl;
  47.         cout<<"Mode = "
  48.             <<(mode==Antenna?"antenna":"cable")<<endl;
  49.         cout<<"Input = "
  50.             <<(input==TV?"TV":"DVD")<<endl;
  51.     }
  52. }
复制代码
  1. //友元类1main.cpp
  2. #include<iostream>
  3. #include"友元类1.h"
  4. int main()
  5. {
  6.     using std::cout;
  7.     Tv s42;
  8.     cout<<"Initial setting for 42" TV:\n";
  9.     s42.settings();
  10.     s42.onoff();
  11.     s42.chanup();
  12.     cout<<"\nAdjusted settings for 42" TV:\n";
  13.     s42.settings();
  14.     Remote grey;
  15.     grey.set_chan(s42,10);
  16.     grey.volup(s42);
  17.     grey.volup(s42);
  18.     cout<<"\n42" settings after using remote:\n";
  19.     s42.settings();
  20.     Tv s58(Tv::On);
  21.     s58.set_mode();
  22.     grey.set_chan(s58,28);
  23.     cout<<"\n58" settings:\n";
  24.     s58.settings();
  25.     return 0;
  26. }
复制代码
  1. PS D:\study\c++\path_to_c++> g++ -I .\include\ -o 友元类1 .\友元类1.cpp .\友元类1main.cppPS D:\study\c++\path_to_c++> .\友元类1.exeInitial setting for 42" TV:  Tv is Off
  2. Adjusted settings for 42" TV:Tv is OnVolume setting = 5Channel setting = 3Mode = cableInput = TV
  3. 42" settings after using remote:Tv is OnVolume setting = 7Channel setting = 10Mode = cableInput = TV
  4. 58" settings:Tv is OnVolume setting = 5Channel setting = 28Mode = antennaInput = TV
复制代码
总之,友元类和友元函数很类似,不需要过多说明了。

2. 友元成员函数

在上面那个例子中,我们知道大部分
  1. Remote
复制代码
方法都是用
  1. Tv
复制代码
类的公有接口实现的。这意味着这些方法不是真正需要作为友元。事实上,只有一个直接访问
  1. Tv
复制代码
的私有数据的
  1. Remote
复制代码
方法即
  1. Remote::chan()
复制代码
,因此它才是唯一作为友元的方法。我们可以选择仅让特定的类成员成为另一个类的友元,而不必让整个类成为友元,但这样做会有一些麻烦。
  1. Remote::chan()
复制代码
成为
  1. Tv
复制代码
类的友元的方法是,在
  1. Tv
复制代码
类声明中将其声明为友元:
  1. class Tv
  2. {
  3.     friend void Remote::set_chan(Tv & t,int c);
  4.     ...
  5. }
复制代码
但是,编译器能处理这条语句,它必须知道
  1. Remote
复制代码
的定义。否则,它就不知道
  1. Remote::set_chan
复制代码
是什么东西。所以我们必须把
  1. Remote
复制代码
的声明放到
  1. Tv
复制代码
声明的前面。但是
  1. Remote
复制代码
声明中同样提到了
  1. TV
复制代码
类,那么我们必须把
  1. TV
复制代码
声明放到
  1. Remote
复制代码
声明的前面。这就发生了循环依赖。我们得使用 前向声明(forward declaration) 来解决这一问题。
  1. class Tv;
  2. class Remote{...};
  3. class Tv{...};
复制代码
但是,还有一点麻烦需要解决:
  1. Remote
复制代码
的类声明中不能直接给出成员函数的定义了,因为这些函数会访问
  1. Tv
复制代码
类成员,而
  1. Tv
复制代码
类的成员的声明是
  1. Remote
复制代码
类的后面的。那么我们必须在
  1. Remote
复制代码
的类声明外给出方法定义。
代码实现:
  1. //友元成员函数1.h
  2. #ifndef TVFM_H_
  3. #define TVFM_H_
  4. class Tv;
  5. class Remote
  6. {
  7. public:
  8.     enum{Off,On};
  9.     enum{MinVal,MaxVal=20};
  10.     enum{Antenna,Cable};
  11.     enum{TV,DVD};
  12. private:
  13.     int mode;//控制TV or DVD
  14. public:
  15.     Remote(int m=TV):mode(m){};
  16.     bool volup(Tv & t);
  17.     bool voldown(Tv & t);
  18.     void onoff(Tv &t);
  19.     void chanup(Tv &t);
  20.     void chandown(Tv &t);
  21.     void set_chan(Tv &t,int c);
  22.     void set_mode(Tv &t);
  23.     void set_input(Tv &t);
  24. };
  25. class Tv
  26. {
  27. private:
  28.     int state;//On or Off
  29.     int volume;//音量
  30.     int maxchannel;//频道数
  31.     int channel;//频道
  32.     int mode;//有线还是天线,Antenna or Cable
  33.     int input;//TV or DVD
  34. public:
  35.     friend void Remote::set_chan(Tv &t,int c);
  36.     enum{Off,On};
  37.     enum{MinVal,MaxVal=20};
  38.     enum{Antenna,Cable};
  39.     enum{TV,DVD};
  40.     Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc),
  41.                     channel(2),mode(Cable),input(TV){}
  42.     void onoff(){state=(state==On)?Off:On;}
  43.     bool ison() const{return state==On;}
  44.     bool volup();
  45.     bool voldown();
  46.     void chanup();
  47.     void chandown();
  48.     void set_mode(){mode=(mode==Antenna)?Cable:Antenna;}
  49.     void set_input(){input=(input==TV)?DVD:TV;}
  50.     void settings() const;//display all settings
  51. };
  52. inline bool Remote::volup(Tv & t){return t.volup();}
  53. inline bool Remote::voldown(Tv & t){return t.voldown();}
  54. inline void Remote::onoff(Tv &t){t.onoff();}
  55. inline void Remote::chanup(Tv &t){t.chanup();}
  56. inline void Remote::chandown(Tv &t){t.chandown();}
  57. inline void Remote::set_chan(Tv &t,int c){t.channel=c;}
  58. inline void Remote::set_mode(Tv &t){t.set_mode();}
  59. inline void Remote::set_input(Tv &t){t.set_input();}
  60. #endif
复制代码
之前我们说过,内联函数的链接性是内部的,这就意味著函数定义必须在使用函数的文件中。在上面的代码中,内联函数的定义位于头文件中。当然你也可以将定义放在实现文件中,但必须删除关键字
  1. inline
复制代码
,这样函数的链接性将是外部的。
还有就是,我们直接让整个
  1. Remote
复制代码
类成为友元并不需要前向声明,因为友元语句已经指出
  1. Remote
复制代码
是一个类:
  1. friend class Remote;
复制代码

总之,不推荐使用友元成员函数,使用友元类完全可以达到相同的目的。

3. 其他友元关系


3.1 成为彼此的友元类

还是电视机和遥控器的例子,我们知道遥控器能控制电视机,但是我告诉你,现代电视机也是可以控制遥控器的。例如,我们现在可以在电视上玩角色扮演游戏,当你控制的角色从高处落入水中时,遥控器(手柄)会发出振动模拟落水感。那么,遥控器是电视机的友元,电视机也是遥控器的友元,那么它们互为友元。
  1. class Tv
  2. {
  3. friend class Remote;
  4. public:
  5.     void buzz(Remote & r);
  6.     ...
  7. };
  8. class Remote
  9. {
  10. friend class Tv;
  11. public:
  12.     void bool volup(Tv &t){t.volup();}
  13.     ...
  14. };
  15. inline void Tv::buzz(Remote &r)
  16. {
  17.     ...
  18. }
复制代码
这里
  1. buzz
复制代码
函数的定义必须放到
  1. Remote
复制代码
类声明的后面,因为
  1. buzz
复制代码
的定义中会使用到
  1. Remote
复制代码
的成员。

3.2 共同的友元

使用友元的另一种情况是,函数需要访问两个类的私有数据,那么必须这样做:函数既是一个类的友元也是另一个类的友元.
例如,有两个类
  1. Analyzer
复制代码
  1. Probe
复制代码
,我们需要同步它们的时间成员:
  1. class Analyzer;
  2. class Probe
  3. {
  4.     friend void sync(Analyzer & a,const Probe &p);
  5.     friend void sync(Probe &p,const Analyzer &a);
  6. };
  7. class Probe
  8. {
  9.     friend void sync(Analyzer & a,const Probe &p);
  10.     friend void sync(Probe &p,const Analyzer &a);
  11. };
  12. inline void sync(Analyzer & a,const Probe &p)
  13. {
  14.     ...
  15. }
  16. inline void sync(Probe &p,const Analyzer &a)
  17. {
  18.     ...
  19. }
复制代码
4. 嵌套类

在C++中我们可以将类声明放在另一个类中。在另一个类中声明的类被称为嵌套类。
实际上,嵌套类很简单,它的原理和类中声明结构体、常量、枚举、
  1. typedef
复制代码
、名称空间是一样的,这些技术我们一直都在使用。
对类进行嵌套和包含是不一样的。包含意味著将类对象作为另一个类的成员,而对类进行嵌套不创建类成员,而是定义了一种类型,该类型仅在包含嵌套类的类中有效。
一般来说我们使用嵌套类是为了帮助实现另一个类,并避免名称冲突

嵌套类的作用域和访问控制


作用域

如果嵌套类是在另一个类的私有部分声明的,那么只能在后者的类作用域中使用它,派生类以及外部世界无法使用它。
如果嵌套类是在另一个类的保护部分声明的,那么只能在后者、后者的派生类的类作用域中使用该嵌套类,外部世界无法使用它。
如果嵌套类是在另一个类的公有部分声明的,那么能在后者、后者的派生类和外部世界中使用它。
  1. class Team
  2. {
  3. public:
  4.     class Coach{...}
  5.     ...
  6. };
复制代码
上面的
  1. Coach
复制代码
就是一个公有部分的嵌套类,那么我们可以这样:
  1. Team::Coach forhire;
复制代码
总之,嵌套类的作用域和类中声明结构体、常量、枚举、
  1. typedef
复制代码
、名称空间是一样。但是对于枚举量来说,我们一般把它放在类的公有部分,例如
  1. ios_base
复制代码
类中的各种格式常量:
  1. ios_base::showpoint
复制代码
等。
访问控制
嵌套类的访问控制和常规类是一模一样的,嵌套类也有
  1. public
复制代码
,
  1. private
复制代码
,
  1. protected
复制代码
,只有公有部分对外部世界开放。
例如:
  1. class A
  2. {
  3.     class B
  4.     {
  5.     private:
  6.         int num;
  7.     public
  8.         void foo();
  9.     };
  10. };
复制代码
则在A的类作用域中,可以创建B对象,并使用
  1. B.foo()
复制代码
方法。
看看一个类模板中使用嵌套类的例子:
  1. #ifndef QUEUETP_H_
  2. #define QUEUETP_H_
  3. template<typename Item>
  4. class QueueTP
  5. {
  6. private:
  7.     enum{Q_SIZE=10};
  8.     class Node
  9.     {
  10.     public:
  11.         Item item;
  12.         Node *next;
  13.         Node(const Item & i):item(i),next(0){}
  14.     };
  15.     Node *front;
  16.     Node *rear;
  17.     int items;
  18.     const int qsize;
  19.     QueueTP(const QueueTP &q):qsize(0){}//抢占定义,赋值构造函数
  20.     QueueTP & operator=(const QueueTP &q){return *this;}//抢占定义
  21. public:
  22.     QueueTP(int qs=Q_SIZE):qsize(qs)
  23.     {
  24.         front = rear =0;
  25.         items=0;
  26.     }
  27.     ~QueueTP()
  28.     {
  29.         Node* temp;
  30.         while (front !=0)
  31.         {
  32.             temp=front;
  33.             front=front->next;
  34.             delete temp;
  35.         }
  36.     }
  37.     bool isempty() const
  38.     {
  39.         return items==0;
  40.     }
  41.     bool isfull() const
  42.     {
  43.         return items==qsize;
  44.     }
  45.     int queuecount() const
  46.     {
  47.         return items;
  48.     }
  49.     bool enqueue(const Item & item)
  50.     {
  51.         if(isfull())
  52.             return false;
  53.         Node * add = new Node(item);
  54.         items++;
  55.         if(front==0)
  56.             front=add;
  57.         else
  58.             rear->next=add;
  59.         rear=add;
  60.         return true;
  61.     }
  62.     bool dequeue(Item &item)
  63.     {
  64.         if(front==0)
  65.             return 0;
  66.         item=front->item;
  67.         items--;
  68.         Node * temp=front;
  69.         front=front->next;
  70.         delete temp;
  71.         if(items==0)
  72.             rear=0;
  73.         return true;
  74.     }
  75. };
  76. #endif
复制代码
到此这篇关于C++中友元类和嵌套类使用详解的文章就介绍到这了,更多相关C++友元类和嵌套类内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
20
积分
20
注册时间
2022-12-26
最后登录
2022-12-26

发表于 6 天前 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表