C++中友元類和嵌套類使用詳解
前言
友元這個詞,在學(xué)習(xí)類的時候肯定接觸過,但是當(dāng)時我們只用了很多友元函數(shù)。
友元有三種:
- 友元函數(shù)
- 友元類
- 友元類方法
類并非只能擁有友元函數(shù),也可以將類作為友元。在這種情況下,友元類的所以方法都能訪問原始類的私有成員和保護(hù)成員。另外,也可以做更嚴(yán)格的限制,只將特定的成員函數(shù)指定為另一個類的友元。
1. 友元類
假如我們有兩個類:Tv電視機類,Remote遙控器類。那么這兩個類是什么關(guān)系呢?既不是has-a關(guān)系,也不是 is-a關(guān)系,但是我們知道遙控器可以控制電視機,那么遙控器必須能夠訪問電視機的私有或保護(hù)數(shù)據(jù)。所以,遙控器就是電視機的友元。
類似于友元函數(shù)的聲明,友元類的聲明:
friend class Remote;
友元聲明可以位于公有、私有或保護(hù)部分,其所在的位置無關(guān)緊要。
下面是代碼實現(xiàn):
//友元類1.h
#ifndef TV_H_
#define TV_H_
class Tv
{
private:
int state;//On or Off
int volume;//音量
int maxchannel;//頻道數(shù)
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
總之,友元類和友元函數(shù)很類似,不需要過多說明了。
2. 友元成員函數(shù)
在上面那個例子中,我們知道大部分Remote方法都是用Tv類的公有接口實現(xiàn)的。這意味著這些方法不是真正需要作為友元。事實上,只有一個直接訪問Tv的私有數(shù)據(jù)的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聲明的前面。這就發(fā)生了循環(huán)依賴。我們得使用 前向聲明(forward declaration) 來解決這一問題。
class Tv;
class Remote{...};
class Tv{...};
但是,還有一點麻煩需要解決:Remote的類聲明中不能直接給出成員函數(shù)的定義了,因為這些函數(shù)會訪問Tv類成員,而Tv類的成員的聲明是Remote類的后面的。那么我們必須在Remote的類聲明外給出方法定義。
代碼實現(xiàn):
//友元成員函數(shù)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;//頻道數(shù)
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之前我們說過,內(nèi)聯(lián)函數(shù)的鏈接性是內(nèi)部的,這就意味著函數(shù)定義必須在使用函數(shù)的文件中。在上面的代碼中,內(nèi)聯(lián)函數(shù)的定義位于頭文件中。當(dāng)然你也可以將定義放在實現(xiàn)文件中,但必須刪除關(guān)鍵字inline,這樣函數(shù)的鏈接性將是外部的。
還有就是,我們直接讓整個Remote類成為友元并不需要前向聲明,因為友元語句已經(jīng)指出Remote是一個類:
friend class Remote;。
總之,不推薦使用友元成員函數(shù),使用友元類完全可以達(dá)到相同的目的。
3. 其他友元關(guān)系
3.1 成為彼此的友元類
還是電視機和遙控器的例子,我們知道遙控器能控制電視機,但是我告訴你,現(xiàn)代電視機也是可以控制遙控器的。例如,我們現(xiàn)在可以在電視上玩角色扮演游戲,當(dāng)你控制的角色從高處落入水中時,遙控器(手柄)會發(fā)出振動模擬落水感。那么,遙控器是電視機的友元,電視機也是遙控器的友元,那么它們互為友元。
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函數(shù)的定義必須放到Remote類聲明的后面,因為buzz的定義中會使用到Remote的成員。
3.2 共同的友元
使用友元的另一種情況是,函數(shù)需要訪問兩個類的私有數(shù)據(jù),那么必須這樣做:函數(shù)既是一個類的友元也是另一個類的友元.
例如,有兩個類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)
{
...
}4. 嵌套類
在C++中我們可以將類聲明放在另一個類中。在另一個類中聲明的類被稱為嵌套類。
實際上,嵌套類很簡單,它的原理和類中聲明結(jié)構(gòu)體、常量、枚舉、typedef、名稱空間是一樣的,這些技術(shù)我們一直都在使用。
對類進(jìn)行嵌套和包含是不一樣的。包含意味著將類對象作為另一個類的成員,而對類進(jìn)行嵌套不創(chuàng)建類成員,而是定義了一種類型,該類型僅在包含嵌套類的類中有效。
一般來說我們使用嵌套類是為了幫助實現(xiàn)另一個類,并避免名稱沖突
嵌套類的作用域和訪問控制
作用域
如果嵌套類是在另一個類的私有部分聲明的,那么只能在后者的類作用域中使用它,派生類以及外部世界無法使用它。
如果嵌套類是在另一個類的保護(hù)部分聲明的,那么只能在后者、后者的派生類的類作用域中使用該嵌套類,外部世界無法使用它。
如果嵌套類是在另一個類的公有部分聲明的,那么能在后者、后者的派生類和外部世界中使用它。
class Team
{
public:
class Coach{...}
...
};
上面的Coach就是一個公有部分的嵌套類,那么我們可以這樣:
Team::Coach forhire;
總之,嵌套類的作用域和類中聲明結(jié)構(gòu)體、常量、枚舉、typedef、名稱空間是一樣。但是對于枚舉量來說,我們一般把它放在類的公有部分,例如ios_base類中的各種格式常量:ios_base::showpoint等。
訪問控制
嵌套類的訪問控制和常規(guī)類是一模一樣的,嵌套類也有public,private,protected,只有公有部分對外部世界開放。
例如:
class A
{
class B
{
private:
int num;
public
void foo();
};
};則在A的類作用域中,可以創(chuàng)建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){}//搶占定義,賦值構(gòu)造函數(shù)
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到此這篇關(guān)于C++中友元類和嵌套類使用詳解的文章就介紹到這了,更多相關(guān)C++友元類和嵌套類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中的函數(shù)指針與函數(shù)對象的總結(jié)
以下是對C++中的函數(shù)指針與函數(shù)對象的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下2013-07-07
C語言中操作進(jìn)程信號的相關(guān)函數(shù)使用詳解
這篇文章主要介紹了C語言中操作進(jìn)程信號的相關(guān)函數(shù)使用詳解,分別是signal()函數(shù)和kill()函數(shù)的用法,需要的朋友可以參考下2015-09-09
C++結(jié)構(gòu)體中變長數(shù)組的使用問題分解刨析
變長數(shù)組在C++中指的是集合(也叫容器)如vector就是C語言中,所有的數(shù)組都不定長,沒有下標(biāo)越界的概念,數(shù)組實質(zhì)就是一個指針(由數(shù)組名充當(dāng))因此C語言中數(shù)組的長度沒有任何意義平常在C語言中講的不定長數(shù)組,其實就是指針2022-08-08

