C++中友元的实例详解
C++中友元的实例详解
尽管友元被授予从外部访问类的私有部分的权限,但他们并不与面向对象的编程思想相悖;相反他提高了公共接口的灵活性。
一、友元类
友元声明可以位于公有、私有活保护部分、其所在位置无关紧要
我直接贴出一个摘自
其中Remote为Tv的友元类。
Tv.h
#ifndefTV_H_ #defineTV_H_ /*一个类电视*/ classTv { public: friendclassRemote;//Remote类可以访问TvPrivite的私有部分 enum{ off,on//开关 }; enum { MinVal,MaxVal=20//音量 }; enum{ Antena,Cable//使用的天线、还是电缆 }; enum { TV,DVD//工作模式 }; Tv(ints=off,intmc=125):state(s),volume(5),maxchannel(mc), channel(5),mode(Cable),input(TV){} voidonoff(){state=(state==on)?off:on;} boolison()const{returnstate==on;} boolvolup();//增大声音 boolvoldown();//减小声音 voidchanup();//频道+ voidchandown();//频道- voidset_mode(){mode=(mode==Antena)?Cable:Antena;} voidset_input(){input=(input==TV)?DVD:TV;} voidsettings()const;//显示所有设置 private: intstate;//开或者关 intvolume;//音量 intmaxchannel;//最大 intchannel;//当前频道 intmode;//广播还是电缆 intinput;//Tv或者DVD }; /*Remote的定义(遥控器)*/ classRemote{ private: intmode;//控制TV或DVD public: Remote(intm=Tv::TV):mode(m){} boolvolup(Tv&t){returnt.volup();} boolvoldown(Tv&t){returnt.voldown();} voidonoff(Tv&t){returnt.onoff();} voidchanup(Tv&t){returnt.chanup();} voidchandown(Tv&t){returnt.chandown();} voidset_chan(Tv&t,intc){t.channel=c;}//访问了Tv的私有成员 voidset_mode(Tv&t){t.set_mode();} voidset_input(Tv&t){t.set_input();} }; #endif//TV_H_
Tv.cpp
#include"stdafx.h" #include"Tv.h" #includeboolTv::volup(){ if(volume MinVal){ volume--; returntrue; } else{ returnfalse; } } voidTv::chanup(){ if(channel 1)channel--; elsechannel=maxchannel; } voidTv::settings()const{ usingstd::cout; usingstd::endl; cout<<"TVis"<<(state==off?"off":"on")< 测试代码:
#include"stdafx.h" #include"tv.h" #includeintmain() { usingstd::cout; Tvs42; cout<<"Initialsettingsfor42\"Tv:\n"; s42.settings(); s42.onoff(); s42.chanup(); cout<<"\nAdjustedsettingsfor42\"Tv:\n"; s42.chanup(); cout<<"\nAdjustedsettingsfor42\"Tv:\n"; s42.settings(); Remotegrey; grey.set_chan(s42,10); grey.volup(s42); grey.volup(s42); cout<<"\ns42\"settingsafterusingremote:\n"; s42.settings(); Tvs58(Tv::on); s58.set_mode(); grey.set_chan(s58,58); cout<<"\ns58\"setting:\n"; s58.settings(); system("pause"); return0; } 运行结果:
Initialsettingsfor42"Tv: TVisoff Adjustedsettingsfor42"Tv: Adjustedsettingsfor42"Tv: TVison Volumesetting=5 Channelsetting=7 Mode=cable Input=TV s42"settingsafterusingremote: TVison Volumesetting=7 Channelsetting=10 Mode=cable Input=TV s58"setting: TVison Volumesetting=5 Channelsetting=58 Mode=antenna Input=TV 请按任意键继续...上述代码中将Remote类设置成为了Tv类的友元类,但事实上我们看到:唯一访问Tv的成员的方法是voidset_chan(Tv&t,intc){t.channel=c;},因此它是唯一需要友元的方法。因此不必让整个类成为友元,这就引出了我们下面要讲的的友元成员函数。
二、友元成员函数
我们要再Tv中将Remote::set_chan()设置成友元:
clasTv { friendvoidRemote::set_chan(Tv&t,intc); }然而要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类。而set_chan是这个类的方法。这意味着应将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而意味着Tv定义应当位于Remote定义之前,避开这种循环依赖的方法是,使用前向声明。
所以应该这样:classTv;//前向声明 classRemote{...} classTv{...}这里还有一个麻烦就是:
Remote包含了内联代码例如:voidonoff(Tv&t){t.onoff();};
由于这将调用Tv的一个方法,所以编译器此时已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明的后面。这种问题的解决方法是:使用Remote声明中只包含方法声明,并将实际的定义放到Tv类之后。所以最终应该这样:
classTv;//前向声明 classRemote{...}//如要用到Tv只能是方法声明 classTv{...} //接着写Remote的定义这里通过方法定义中使用inline关键字,仍然可以使方法称为内联方法
所以程序最终将tv.h改为:#ifndefTV_H_ #defineTV_H_ classTv;//前向声明 classRemote{ public: enum{ off,on//开关 }; enum { MinVal,MaxVal=20//音量 }; enum{ Antena,Cable//使用的天线、还是电缆 }; enum { TV,DVD//工作模式 }; private: intmode;//控制TV或DVD public: Remote(intm=TV):mode(m){} //用到了Tv只能是声明 boolvolup(Tv&t); boolvoldown(Tv&t); voidonoff(Tv&t); voidchanup(Tv&t); voidchandown(Tv&t); voidset_chan(Tv&t,intc); voidset_mode(Tv&t); voidset_input(Tv&t); }; classTv { public: friendvoidRemote::set_chan(Tv&t,intc);//友元成员函数 enum{ off,on//开关 }; enum { MinVal,MaxVal=20//音量 }; enum{ Antena,Cable//使用的天线、还是电缆 }; enum { TV,DVD//工作模式 }; Tv(ints=off,intmc=125):state(s),volume(5),maxchannel(mc), channel(5),mode(Cable),input(TV){} voidonoff(){state=(state==on)?off:on;} boolison()const{returnstate==on;} boolvolup();//增大声音 boolvoldown();//减小声音 voidchanup();//频道+ voidchandown();//频道- voidset_mode(){mode=(mode==Antena)?Cable:Antena;} voidset_input(){input=(input==TV)?DVD:TV;} voidsettings()const;//显示所有设置 private: intstate;//开或者关 intvolume;//音量 intmaxchannel;//最大 intchannel;//当前频道 intmode;//广播还是电缆 intinput;//Tv或者DVD }; inlineboolRemote::volup(Tv&t){returnt.volup();} inlineboolRemote::voldown(Tv&t){returnt.voldown();} inlinevoidRemote::onoff(Tv&t){returnt.onoff();} inlinevoidRemote::chanup(Tv&t){returnt.chanup();} inlinevoidRemote::chandown(Tv&t){returnt.chandown();} inlinevoidRemote::set_chan(Tv&t,intc){t.channel=c;} inlinevoidRemote::set_mode(Tv&t){returnt.set_mode();} inlinevoidRemote::set_input(Tv&t){returnt.set_input();} #endif//TV_H_测试结果不变。
*另外:也可一个将内联函数放在tv.cpp中,但必须去掉inline关键字,这样函数的连接性将成为外部的。
三、其他友元关系
1、上面的代码表示的是Remote是Tv的友元。但我们有时也会用到2个类互相友元。即Remote是Tv的友元,同时Tv又是Remote的友元
他们定义与下面类似:
classRemote classTv { friendclasRemote public: voidbuzz(Remote&r); ... } classRemote { friendclassTv; public: voidBoolvolup(Tv&t){t.volup();} ... } inlinevoidTv::buzz(Remote&r) { ... }由于Remote的声明位于Tv声明的后面,所以可以在类的定义Remote::volup(),但Tv::buzz()方法必须在Tv声明的外部定义,使其位于Remote声明的外面。如果不希望buzz()是内联的,则应在一个单独的方法定义文件中定义它。
2、共同的友元。
需要使用友元的另一种情况是,函数需要访问两个类的私有数据。它可以是一个类的友元,同时是另一个类的友元。示例如下:
classAnalyzer; classProbe { friendvoidsync(Analyzer&a,constProbe&p); friendvoidsync(Probe&p,constAnalyzer&a); ... }; classAnalyzer { friendvoidsync(Analyzer&a,constProbe&p); friendvoidsync(Probe&p,constAnalyzer&a); } inlinevoidsync(Analyzer&a,constProbe&p) { ... } inlinevoidsync(Probe&p,constAnalyzer&a) { ... }如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!