友元類所有的方法都可以訪問原始類的私有成員和保護成員
聲明
friend class ClassName
友元聲明可以位於公有、私有或保護部分,其所在的位置無關緊要。由於Remote類提到了Tv類,
所以編譯器必須了解Tv類后才能處理Remote類,為些最簡單的方法是首先定義Tv類。也可以使用前向聲明,稍后介紹
#include<iostream> using std::cout; using std::cin; using std::endl; class Tv { private: int mode; int volume; public: friend class Remote; Tv(){}; void Show() { cout<<"welcome Tv "<<mode<<endl; } void set_mode(int i){ mode=i; cout<<" mode = "<<mode<<endl; }; }; class Remote { private: int mode; public: void volup(Tv &t,int i) { t.volume=i; cout<<" current volume "<<t.volume<<endl; }; void ShowTv(Tv &t) { t.Show(); } }; int main() { Tv _t; _t.set_mode(3); Remote _r; _r.volup(_t,2); _r.ShowTv(_t); cin.get(); }
上邊的例子中代碼真正訪問Tv成員的是Remote方法是volup,其它的方法不是真需要作為友元的。因此它是唯一需要作為友元的方法
確實可以選擇僅讓特定的類成員成為另一個類的友元,需不必須讓整個類成為友元,但這樣稍微有點麻煩,必須小心排列各種聲明和定義的順序。
讓Remote::volup()成為Tv類的友元的方法,在Tv類聲明中將其聲明為友元
class
{
friend void Remote::set_chan(Tv & t,int c);
};
然而,要使編譯器能夠處理這條語句,它必須知道Remote的定義,否則就無法知道Remote是一個類,而set_chan是這個類的方法。
這意味着就將Remote的定義放到Tv的定義前面。Remote的方法提到了Tv對象,而這意味着Tv定義應當位於Remote定義之前。避開這種循環
依賴的方法是,使用前向聲明(forward declaration)為此,需要在Remote定義的前面插入下面的語句:
class Tv
這樣排列次序應如下:
class Tv;
class Remote{...};
class Tv {....};
還有一個麻煩。程序清單Remote聲明包含了內聯代碼,例如:
void volup(Tv &t,int i)
{
t.volume=i;
cout<<" current volume "<<t.volume<<endl;
};
由於這將調用Tv的一個方法,所以編譯器此時必須已經看到了Tv類的聲明,這樣才能知道Tv有哪些方法,但正如看到的,該聲明位於Remote聲明方法后
。這種問題的解決方法是,使用Remote聲明中只包含方法聲明,並將實際的定義放在Tv類之后。這樣
Remote方法的原型與下面類似:
void volup(Tv &t,int i);
檢查該原型時,所有的編譯器都需要知道Tv是一個類,而前向聲明提供了這樣的信息。當編譯器到達真正的方法定義時,它已經讀取了Tv類的聲明,
並擁有了編譯這些方法所需的信息。能過在方法定義中使用inline關鍵字,仍然可以使其成為內聯方法。
#include<iostream> using std::cout; using std::cin; using std::endl; class Tv; class Remote { private: int mode; public: void volup(Tv &t,int i); void ShowTv(Tv &t); }; class Tv { private: int mode; int volume; public: friend void Remote::volup(Tv & t,int c); Tv(){}; void Show() { cout<<"welcome Tv "<<mode<<endl; } void set_mode(int i){ mode=i; cout<<" mode = "<<mode<<endl; }; }; inline void Remote::volup(Tv & t,int i) { t.volume=i; cout<<" current volume "<<t.volume<<endl; }; inline void Remote::ShowTv(Tv & t) { t.Show(); }; int main() { Tv _t; _t.set_mode(3); Remote _r; _r.volup(_t,2); _r.ShowTv(_t); cin.get(); }
內聯函數的鏈接性是內部的,這意味着函數定義必須在使用函數的文件中,這個例子中內聯定義位於頭文件中,因此在使用函數的文件中包含
頭文件可確保將定義放在正確的地方。這可以將定義放在實現文件中,但必須刪除關鍵字inline這樣函數的鏈接性將是外部的
再看一個操作符重載的小例子
#include<iostream> class B { public : B() { myValue=2; std::cout<<"B init"<<std::endl; } ~B() { std::cout<<"B end"<<std::endl; } //這樣做可以 /*B operator+(const B av) { B a; a.myValue=myValue+av.myValue; return a; }*/ //也可以這么做 friend B operator+(const B b1,const B b2); //------------------------------------------------ int GetMyValue() { return myValue; } //重載<< friend std::ostream& operator<<(std::ostream& os,B); private : int myValue; }; B operator+(const B b1,const B b2) { B a; a.myValue=b1.myValue+b2.myValue; return a; } std::ostream& operator<<(std::ostream& os,B b) { return os<<"重載實現:"<<b.myValue<<std::endl; } int main() { B b1; std::cout<<b1; B b2; B b3=b1+b2; std::cout<<b3<<std::endl; std::cin.get(); return 0; }