天天看點

C++友元函數(類)用法

1,friend 申明一個友元

friend 一般為一句申明式,它位于一個類的内部,它申明一個類或者一個函數為該類的友元。friend 并不是定義一個成員函數,是以 friend 放在 public,protected 或者 private 前都可以,完全是一樣的。做為一個友元,即表示在該類或者該函數内部可以通路這個類的私有成員(通過接口傳過來的類,以及函數體裡面的類,都可以像成員函數一樣通路私有成員),你和朋友之間是不是應該沒有什麼隐藏的呢。例子:

class A
{
public:
A(int _a) : a(_a) {}
friend void test(A&);
friend class B;
private:
int a;
};
void test(A& x)
{
x.a=100;//擁有私有成員 a 的通路權限
}
class B
{
public:
void foo();
};
           

如果 friend 申明式為一般的非模闆類或者函數,則該處可以為首次申明。對于一個類,隻能是申明式,對于函數,可以是它的定義式。

class A
{
public:
A(int _a) : a(_a) {}
friend void test(A& x)
{
x.a = 100;//定義::test()友元函數
}
friend class B;
private:
int a;
};
           

注意盡管将函數的定義式放在類内部,但它并不是一個成員函數,對于省略受限的定義形式它将成為一個全局函數::test(),當然你也可以申明另外一個類的成員函數為友元,如:

class A
{
public:
A(int _a) : a(_a) {}
friend void B::foo();
private:
int a;
};
           

總的來說,如果你想在哪裡通路類 A 的私有成員,就在類 A 内寫上一句該處的申明式,并在前面加上 friend 關鍵字。這是一般情況,很簡單,但是它會破壞封裝的初衷,是以盡量少用;Effective C++中有一個應用的例子,對一個類定義的二進制操作符,如果你希望它能對操作數都進行隐式轉化,那麼就定義一個全局函數,并申明成該類的友元。

2,模闆函數作友元

先給一個模闆函數,它是一個模闆,并不是一個函數:

template<typename T>
void foo1(T);
           

在定義 foo1 為某類的友元時,或者要執行個體化模闆參數 T,或者給出可演繹的申明式,而且就算是可以演繹的,一對尖括号也不能省。如:

class A
{
public:
friend void foo1<char>(char);
friend void foo1<>(double);
};
或者給出限制符:::
class A
{
public:
friend void ::foo1(char);
};
           

當然,如果有一般函數具有這種形式,那會優先于模闆函數比對。最後這裡的申明式都不能是定義式,必須前至申明(定義)。

3,模闆類裡的友元

模闆類裡也能申明 2 中的友元,但是模闆類有模闆參數,如果利用了這個模闆參數的友元申明,就屬這種情形。

template<typename T>
class A
{
public:
friend void foo1<T>(T);
};
           

但是,在這裡,必須要求 foo1 在這裡是可見的,即不能是首次申明式。如果不使用模闆參數,那會是一種有趣的情形。

template<typename T>
class A
{
public:
friend void foo(){}
};
           

注意這裡是一個定義式,它定義了一個::foo()函數為該模闆類的友元,在該模闆類具現的時候,::foo()也被具現出來,即:

A<int> a1;// ::foo()首次定義
A<char> a2;// ::foo()第二次定義,違背 C++一次定義原則
           

4,友元模闆

如果想定義一系列函數為該類的友元,可以使用友元模闆。它和模闆的申明式類似,隻是在template<>後加了 friend 關鍵字。

class A
{
public:
template<typename T>
friend void foo();
};
           

5,能否做為定義式 

能做為定義式的情況是:非受限,沒有前至::,沒有模闆參數清單,沒一對尖括号。如果是模闆申明式,不能是首次申明,在該處必須是可見的。

6,一個完整的例子

template<typename T>
class Rat
{
public:
Rat(T _a, T _b) : a(_a), b(_b) {}
friend Rat<T> operator*<T>(Rat<T>&,Rat<T>&);
private:
T a,b;
};
template<typename T>
Rat<T> operator*(Rat<T> & x, Rat<T> & y)
{
return Rat<T>(x.a*y.a,x.b*y.b);
}
           

Rat<T>為 T 類型的有理數類,定義它的相乘運算,定義一個全局函數,并申明為友元,該函數也應該是模闆,希望有如上的程式通過編譯。在 friend 式之前沒有 operator*()的申明,是以這裡不能是首次申明,在前面必須加上申明式:

template<typename T>
Rat<T> operator*(Rat<T> & x, Rat<T> & y);
           

在這之前又沒有 Rat<T>的申明,再加上:

template<typename T>
class Rat;
           

通過編譯,或者改成友元模闆:

template<typename T>
class Rat
{
public:
Rat(T _a, T _b) : a(_a), b(_b) {}
template<typename U>
friend Rat<U> operator*(Rat<U>&,Rat<U>&);
private: 
T a,b;
};
template<typename T>
Rat<T> operator*(Rat<T> & x, Rat<T> & y)
{
return Rat<T>(x.a*y.a,x.b*y.b);
}
           

有細微的不同,Rat<T>申明了一系列友元 operator*<U>,當然沒必要,隻要 operator*<T>就夠了,但是形式上簡單一些。還有一種更簡單的形式,就是将定義式放在裡面,正是 Effective C++裡使用的方法:

template<typename T>
class Rat
{
public:
Rat(T _a, T _b) : a(_a), b(_b) {}
friend Rat<T> operator*(Rat<T>&x,Rat<T>&y) //定義并申明了::operator*()
{
return Rat<T>(x.a*y.a,x.b*y.b);
}
private:
T a,b;
};
           

繼續閱讀