您好,登錄后才能下訂單哦!
今天小編給大家分享一下C++中友元類和嵌套類如何使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
友元這個詞,在學習類的時候肯定接觸過,但是當時我們只用了很多友元函數。
友元有三種:
友元函數
友元類
友元類方法
類并非只能擁有友元函數,也可以將類作為友元。在這種情況下,友元類的所以方法都能訪問原始類的私有成員和保護成員。另外,也可以做更嚴格的限制,只將特定的成員函數指定為另一個類的友元。
假如我們有兩個類:Tv
電視機類,Remote
遙控器類。那么這兩個類是什么關系呢?既不是has-a關系,也不是 is-a關系,但是我們知道遙控器可以控制電視機,那么遙控器必須能夠訪問電視機的私有或保護數據。所以,遙控器就是電視機的友元。
類似于友元函數的聲明,友元類的聲明:
friend class Remote;
友元聲明可以位于公有、私有或保護部分,其所在的位置無關緊要。
下面是代碼實現:
//友元類1.h #ifndef TV_H_ #define TV_H_ class Tv { private: int state;//On or Off int volume;//音量 int maxchannel;//頻道數 int channel;//頻道 int mode;//有線還是天線,Antenna or Cable int input;//TV or DVD public: friend class Remote; enum{Off,On}; enum{MinVal,MaxVal=20}; enum{Antenna,Cable}; enum{TV,DVD}; Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc), channel(2),mode(Cable),input(TV){} void onoff(){state=(state==On)?Off:On;} bool ison() const{return state==On;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode(){mode=(mode==Antenna)?Cable:Antenna;} void set_input(){input=(input==TV)?DVD:TV;} void settings() const;//display all settings }; class Remote { private: int mode;//控制TV or DVD public: Remote(int m=Tv::TV):mode(m){}; bool volup(Tv & t){return t.volup();} bool voldown(Tv & t){return t.voldown();} void onoff(Tv &t){t.onoff();} void chanup(Tv &t){t.chanup();} void chandown(Tv &t){t.chandown();} void set_chan(Tv &t,int c){t.channel=c;} void set_mode(Tv &t){t.set_mode();} void set_input(Tv &t){t.set_input();} }; #endif
//友元類1.cpp #include"友元類1.h" #include<iostream> bool Tv::volup() { if(volume<MaxVal) { volume++; return true; } else return false; } bool Tv::voldown() { if(volume>MinVal) { volume--; return true; } else return false; } void Tv::chanup() { if(channel<maxchannel) channel++; else channel=1; } void Tv::chandown() { if(channel>1) channel--; else channel=maxchannel; } void Tv::settings() const { using std::cout; using std::endl; cout<<"Tv is "<<(state==Off? "Off":"On")<<endl; if(state==On) { cout<<"Volume setting = "<<volume<<endl; cout<<"Channel setting = "<<channel<<endl; cout<<"Mode = " <<(mode==Antenna?"antenna":"cable")<<endl; cout<<"Input = " <<(input==TV?"TV":"DVD")<<endl; } }
//友元類1main.cpp #include<iostream> #include"友元類1.h" int main() { using std::cout; Tv s42; cout<<"Initial setting for 42\" TV:\n"; s42.settings(); s42.onoff(); s42.chanup(); cout<<"\nAdjusted settings for 42\" TV:\n"; s42.settings(); Remote grey; grey.set_chan(s42,10); grey.volup(s42); grey.volup(s42); cout<<"\n42\" settings after using remote:\n"; s42.settings(); Tv s58(Tv::On); s58.set_mode(); grey.set_chan(s58,28); cout<<"\n58\" settings:\n"; s58.settings(); return 0; }
PS D:\study\c++\path_to_c++> g++ -I .\include\ -o 友元類1 .\友元類1.cpp .\友元類1main.cpp
PS D:\study\c++\path_to_c++> .\友元類1.exe
Initial setting for 42" TV:
Tv is OffAdjusted settings for 42" TV:
Tv is On
Volume setting = 5
Channel setting = 3
Mode = cable
Input = TV42" settings after using remote:
Tv is On
Volume setting = 7
Channel setting = 10
Mode = cable
Input = TV58" settings:
Tv is On
Volume setting = 5
Channel setting = 28
Mode = antenna
Input = TV
總之,友元類和友元函數很類似,不需要過多說明了。
在上面那個例子中,我們知道大部分Remote
方法都是用Tv
類的公有接口實現的。這意味著這些方法不是真正需要作為友元。事實上,只有一個直接訪問Tv
的私有數據的Remote
方法即Remote::chan()
,因此它才是唯一作為友元的方法。我們可以選擇僅讓特定的類成員成為另一個類的友元,而不必讓整個類成為友元,但這樣做會有一些麻煩。
讓Remote::chan()
成為Tv
類的友元的方法是,在Tv
類聲明中將其聲明為友元:
class Tv { friend void Remote::set_chan(Tv & t,int c); ... }
但是,編譯器能處理這條語句,它必須知道Remote
的定義。否則,它就不知道Remote::set_chan
是什么東西。所以我們必須把Remote
的聲明放到Tv
聲明的前面。但是Remote
聲明中同樣提到了TV
類,那么我們必須把TV
聲明放到Remote
聲明的前面。這就發生了循環依賴。我們得使用 前向聲明(forward declaration) 來解決這一問題。
class Tv; class Remote{...}; class Tv{...};
但是,還有一點麻煩需要解決:Remote
的類聲明中不能直接給出成員函數的定義了,因為這些函數會訪問Tv
類成員,而Tv
類的成員的聲明是Remote
類的后面的。那么我們必須在Remote
的類聲明外給出方法定義。
代碼實現:
//友元成員函數1.h #ifndef TVFM_H_ #define TVFM_H_ class Tv; class Remote { public: enum{Off,On}; enum{MinVal,MaxVal=20}; enum{Antenna,Cable}; enum{TV,DVD}; private: int mode;//控制TV or DVD public: Remote(int m=TV):mode(m){}; bool volup(Tv & t); bool voldown(Tv & t); void onoff(Tv &t); void chanup(Tv &t); void chandown(Tv &t); void set_chan(Tv &t,int c); void set_mode(Tv &t); void set_input(Tv &t); }; class Tv { private: int state;//On or Off int volume;//音量 int maxchannel;//頻道數 int channel;//頻道 int mode;//有線還是天線,Antenna or Cable int input;//TV or DVD public: friend void Remote::set_chan(Tv &t,int c); enum{Off,On}; enum{MinVal,MaxVal=20}; enum{Antenna,Cable}; enum{TV,DVD}; Tv(int s=Off,int mc=125):state(s),volume(5),maxchannel(mc), channel(2),mode(Cable),input(TV){} void onoff(){state=(state==On)?Off:On;} bool ison() const{return state==On;} bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode(){mode=(mode==Antenna)?Cable:Antenna;} void set_input(){input=(input==TV)?DVD:TV;} void settings() const;//display all settings }; inline bool Remote::volup(Tv & t){return t.volup();} inline bool Remote::voldown(Tv & t){return t.voldown();} inline void Remote::onoff(Tv &t){t.onoff();} inline void Remote::chanup(Tv &t){t.chanup();} inline void Remote::chandown(Tv &t){t.chandown();} inline void Remote::set_chan(Tv &t,int c){t.channel=c;} inline void Remote::set_mode(Tv &t){t.set_mode();} inline void Remote::set_input(Tv &t){t.set_input();} #endif
之前我們說過,內聯函數的鏈接性是內部的,這就意味著函數定義必須在使用函數的文件中。在上面的代碼中,內聯函數的定義位于頭文件中。當然你也可以將定義放在實現文件中,但必須刪除關鍵字inline
,這樣函數的鏈接性將是外部的。
還有就是,我們直接讓整個Remote
類成為友元并不需要前向聲明,因為友元語句已經指出Remote
是一個類:
friend class Remote;
。
總之,不推薦使用友元成員函數,使用友元類完全可以達到相同的目的。
還是電視機和遙控器的例子,我們知道遙控器能控制電視機,但是我告訴你,現代電視機也是可以控制遙控器的。例如,我們現在可以在電視上玩角色扮演游戲,當你控制的角色從高處落入水中時,遙控器(手柄)會發出振動模擬落水感。那么,遙控器是電視機的友元,電視機也是遙控器的友元,那么它們互為友元。
class Tv { friend class Remote; public: void buzz(Remote & r); ... }; class Remote { friend class Tv; public: void bool volup(Tv &t){t.volup();} ... }; inline void Tv::buzz(Remote &r) { ... }
這里buzz
函數的定義必須放到Remote
類聲明的后面,因為buzz
的定義中會使用到Remote
的成員。
使用友元的另一種情況是,函數需要訪問兩個類的私有數據,那么必須這樣做:函數既是一個類的友元也是另一個類的友元.
例如,有兩個類Analyzer
和Probe
,我們需要同步它們的時間成員:
class Analyzer; class Probe { friend void sync(Analyzer & a,const Probe &p); friend void sync(Probe &p,const Analyzer &a); }; class Probe { friend void sync(Analyzer & a,const Probe &p); friend void sync(Probe &p,const Analyzer &a); }; inline void sync(Analyzer & a,const Probe &p) { ... } inline void sync(Probe &p,const Analyzer &a) { ... }
在C++中我們可以將類聲明放在另一個類中。在另一個類中聲明的類被稱為嵌套類。
實際上,嵌套類很簡單,它的原理和類中聲明結構體、常量、枚舉、typedef
、名稱空間是一樣的,這些技術我們一直都在使用。
對類進行嵌套和包含是不一樣的。包含意味著將類對象作為另一個類的成員,而對類進行嵌套不創建類成員,而是定義了一種類型,該類型僅在包含嵌套類的類中有效。
一般來說我們使用嵌套類是為了幫助實現另一個類,并避免名稱沖突
如果嵌套類是在另一個類的私有部分聲明的,那么只能在后者的類作用域中使用它,派生類以及外部世界無法使用它。
如果嵌套類是在另一個類的保護部分聲明的,那么只能在后者、后者的派生類的類作用域中使用該嵌套類,外部世界無法使用它。
如果嵌套類是在另一個類的公有部分聲明的,那么能在后者、后者的派生類和外部世界中使用它。
class Team { public: class Coach{...} ... };
上面的Coach
就是一個公有部分的嵌套類,那么我們可以這樣:
Team::Coach forhire;
總之,嵌套類的作用域和類中聲明結構體、常量、枚舉、typedef
、名稱空間是一樣。但是對于枚舉量來說,我們一般把它放在類的公有部分,例如ios_base
類中的各種格式常量:ios_base::showpoint
等。
訪問控制
嵌套類的訪問控制和常規類是一模一樣的,嵌套類也有public
,private
,protected
,只有公有部分對外部世界開放。
例如:
class A { class B { private: int num; public void foo(); }; };
則在A的類作用域中,可以創建B對象,并使用B.foo()
方法。
看看一個類模板中使用嵌套類的例子:
#ifndef QUEUETP_H_ #define QUEUETP_H_ template<typename Item> class QueueTP { private: enum{Q_SIZE=10}; class Node { public: Item item; Node *next; Node(const Item & i):item(i),next(0){} }; Node *front; Node *rear; int items; const int qsize; QueueTP(const QueueTP &q):qsize(0){}//搶占定義,賦值構造函數 QueueTP & operator=(const QueueTP &q){return *this;}//搶占定義 public: QueueTP(int qs=Q_SIZE):qsize(qs) { front = rear =0; items=0; } ~QueueTP() { Node* temp; while (front !=0) { temp=front; front=front->next; delete temp; } } bool isempty() const { return items==0; } bool isfull() const { return items==qsize; } int queuecount() const { return items; } bool enqueue(const Item & item) { if(isfull()) return false; Node * add = new Node(item); items++; if(front==0) front=add; else rear->next=add; rear=add; return true; } bool dequeue(Item &item) { if(front==0) return 0; item=front->item; items--; Node * temp=front; front=front->next; delete temp; if(items==0) rear=0; return true; } }; #endif
以上就是“C++中友元類和嵌套類如何使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。