在C++中,在某些情況下,允許特定的非成員函數訪問一個類的私有成員,同時仍然阻止一般的訪問,這是很方便做到的。例如,被重載的操作符,如輸入或輸出操作符,經常需要訪問類的私有數據成員。這些操作符不可能為類的成員。然而,盡管不是類的成員,它們仍是類的“接口的組成部分”。
而友元機制,允許一個類將對其非公有成員的訪問權授予指定的函數或類。友元的聲明以關鍵字 friend 開始。它只能出現在類定義的內部。友元聲明可以出現在類中的任何地方:友元不是授予友元關系的那個類的成員,所以它們不受聲明出現部分的訪問控制影響。
下面給出C++中所有有關友元的結論和限制,然后后面針對每個細節給出示例:
1、友元關系:
1.1將一個非成員函數reset()聲明為類example的友元函數,使得該非成員函數可以訪問類example的私有成員。
class example; // 這里必須對類 example 做前向聲明,否則下面的函數聲明將報錯
void reset(example &e);
class example
{
public:
friend void reset(class example &e);
private:
int n;
};
// 該函數定義必須放在類 example 的后面,否則無法解析變量n
void reset(example &e)
{
e.n = 0;
}
1.2 將類man聲明為類woman的友元類,使得可以通過類man對象訪問類woman的私有成員。
class woman; // 前向聲明
class man
{
public:
void disp(woman &w);
void reset(woman &w);
};
class woman
{
public:
friend class man; // 將man設為woman的友元類,這樣man對象的任何成員函數都可以訪問woman的私有成員
private:
string name;
};
void man::disp(woman &w)
{
cout << w.name << endl;
}
void man::reset(woman &w)
{
w.name.clear();
}
1.3 將一個類Y的某成員函數聲明為類X的友元函數,使得可以通過類Y的這個成員函數訪問類X的私有成員。
class woman; // 前向聲明
class man
{
public:
void disp(woman &w);
void reset(woman &w);
};
class woman
{
public:
friend void man::disp(woman &w); // 將man的其中一個成員函數disp()設為woman的友元函數,就可以使用該函數訪 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 問woman對象的私有成員了
private:
string name;
};
void man::disp(woman &w)
{
cout << w.name << endl;
}
// man的reset()成員函數不是woman類的友元函數,因此不能訪問其私有成員
/*
void man::reset(woman &w)
{
w.name.clear();
}
*/
2、友元作用域
注意到上面的示例代碼,友元聲明和友元定義之間的依賴關系:比如最后一個例子,由于woman類要聲明man類中的成員函數disp為友元函數,因此必須將man類的定義放在woman之前。但是man類中又要對woman類中的數據成員進行操作,所以必須將disp函數的定義放在woman的后面。這個關系要搞清楚,否則編譯不成功。
3、友元跟重載函數的關系
如果有多個重載函數的版本,那么可以將其中的一個或者幾個設為某個類的友元。其他的函數不受此設置的影響,依然不能訪問某類的私有數據成員。例如:
#include
#include
#include
using namespace std;
class screen;
void show(const screen &s);
void show(int, int, const screen &);
class screen
{
public:
screen():x(0.0), y(0.0){}
friend void show(const screen &s);
friend void show(int a, int b, const screen &s); // 如果將此行刪除,將無法調用該版本的show函數
private:
float x;
float y;
};
void show(const screen &s)
{
cout << s.x << ", " << s.y << endl;
}
void show(int a, int b, const screen &s)
{
cout << s.x + a << ", " << s.y + b << endl;
}
int main(void)
{
screen s;
show(s);
show(100, 200, s);
string word;
getline(cin, word);
return 0;
}
4、友元跟權限標識符的關系
實際上,一個類的友元,不管是友元類、友元成員函數還是友元非成員函數,都必須是public訪問權限的,否則無法在類外被調用。