歡迎轉載,但請標明作者 “九天雁翎”,當然,你給出這個文章的連結更好。
呵呵,又來了,自從我開始嘗試描述類以來,我發現我自己是開始真的瞭解類了,雖然還不到就明白什麼叫oo的高深境界,起碼對於類的使用方法瞭解的更多了,希望你看了以後也能有所進步啊:)
現在開始講一個有利有弊的東西,友元(friend),我以前講過了private的資料和函數別人是不能直接調用的,這一點對於封裝起到了很重要的作用。但是有的時候總是有調用一個類private成員這樣需要的,那怎麼辦呢?C++給了我們友元這個傢伙,按我的習慣,首先看個例子。當然,還是我們的水果類:)
例5.1:
#include <string>
#include <iostream>
using namespace std;
class Fruit //定義一個類,名字叫Fruit
{
string name; //定義一個name成員
string colour; //定義一個colour成員
friend bool isSame(Fruit &,Fruit &); //在這裡聲明friend友元函數
public:
void print() //定義一個輸出名字的成員print()
{
cout<<colour<<" "<<name<<endl;
}
Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){} //建構函式
Fruit(){}
};
bool isSame(Fruit &aF,Fruit &bF)
{
return aF.name == bF.name; //注意,這個函數調用了Fruit的private資料,本來可是不允許的.
}
int main()
{
Fruit apple;
Fruit apple2(apple);
Fruit orange("orange","yellow");
cout<<"apple = orange ?: "<<isSame(apple,orange)<<endl;
cout<<"apple = apple2 ?: "<<isSame(apple,apple2)<<endl;
return 0;
}
這裡,我們聲明了一個isSame()檢測是否同名的函數,而且這不是Fruit類的一個函數,雖然他在類裡面聲明了,怎麼看出來?假如是類的成員函數,在外部定義必須要Fruit::這樣定義,不是嗎?isSame()沒有這樣,他是個獨立的函數,但是他可以調用Fruit類的私人成員,因為在類裡聲明了他是Friend的,這就像你告訴保安(編譯器)某某(isSame)是你的朋友(friend),然後讓他可以進入你的家(調用私人成員)一樣,別人就不允許(非友元函數不允許),這樣說,夠明白嗎?你可以嘗試去掉friend聲明看看編譯錯誤。證明friend的作用:)我這裡的得出的編譯錯誤是這樣(error C2248: 'Fruit::name' : cannot access private member declared in class 'Fruit'),也就是name是私人成員,不能調用。不僅可以聲明友元函數,還可以聲明友元類。效果類似。看下面的例子。
例5.2:
#include <string>
#include <iostream>
using namespace std;
class Fruit //定義一個類,名字叫Fruit
{
string name; //定義一個name成員
string colour; //定義一個colour成員
friend class Person; //聲明一個友元類Person
public:
void print() //定義一個輸出名字的成員print()
{
cout<<colour<<" "<<name;
}
Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){} //建構函式
};
class Person //定義類Person
{
string likedFruit;
public:
string name;
bool isLike(Fruit &aF)
{
return likedFruit == aF.name; //注意,他調用了Fruit類的私人成員,這本來是不允許的
}
Person(const string &npe = "jim",const string &lF = "apple"):name(npe),likedFruit(lF){}
};
int main()
{
Fruit apple;
Fruit orange("orange","yellow");
Person jim;
cout<<"Is "<<jim.name<<"like ";
apple.print();
cout<<"? : "<< jim.isLike(apple)<<endl; //看看這個函數的調用
return 0;
}
bool isSame(Fruit &aF,Fruit &bF,Fruit &cF)
{
return (aF.name == bF.name)&&(bF.name == cF.name);
}
具體Person類和程式到底是什麼意思,我想只要你看了我以前寫得東西,應該很明白了,就不多注釋和講了,我現在主要是講友元(friend)的用途。另外一點重載函數的話,你想讓幾個成為友元就讓幾個,其他的將不是友元函數,這裡提醒一下,重載函數其實可是各自獨立的函數,只不過在C++中為了調用方便,讓他們叫同一個名字而已。你不相信,可以自己試試。比如說在例5.1中,假如你重載一個但是卻不聲明為友元,編譯是通不過的。你必須這樣各自聲明。見下例。
例5.3:
#include <string>
#include <iostream>
using namespace std;
class Fruit //定義一個類,名字叫Fruit
{
string name; //定義一個name成員
string colour; //定義一個colour成員
friend bool isSame(Fruit &aF,Fruit &bF); //聲明為友元
friend bool isSame(Fruit &aF,Fruit &bF,Fruit &cF); //再次聲明為友元
public:
void print() //定義一個輸出名字的成員print()
{
cout<<colour<<" "<<name;
}
Fruit(const string &nst = "apple",const string &cst = "green"):name(nst),colour(cst){} //建構函式
};
bool isSame(Fruit &aF,Fruit &bF)
{
return aF.name == bF.name;
}
bool isSame(Fruit &aF,Fruit &bF,Fruit &cF)
{
return (aF.name == bF.name)&&(bF.name == cF.name);
}
int main()
{
Fruit apple;
Fruit apple2;
Fruit apple3;
Fruit orange("orange","yellow");
cout<<isSame(apple,apple2)<<isSame(apple,apple2,orange)<<endl;
return 0;
}
現在再回過來看例4.0。
#include <string>
#include <iostream>
using namespace std;
class Fruit //定義一個類,名字叫Fruit
{
string name; //定義一個name成員
string colour; //定義一個colour成員
public:
bool isSame(const Fruit &otherFruit) //期待的形參是另一個Fruit類對象,測試是否同名
{
return name == otherFruit.name;
}
void print() //定義一個輸出名字的成員print()
{
cout<<colour<<" "<<name<<endl;
}
Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst){} //建構函式
Fruit(){}
};
int main()
{
Fruit apple("apple");
Fruit orange("orange");
cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl; //沒有問題,肯定不同
cout<<"apple = /"apple/" ?:"<<apple.isSame(string("apple")); //用一個string做形參?
return 0;
}
除了隱式類類型轉換外你還發現什麼沒有?恩,就是isSame()函數他直接調用了另一個引用的Fruit對象的私人成員name,這個按道理是不允許的啊,不過,注意的是,他本身就是Fruit類,所以,我個人看法(純粹個人看法),這裡可以認為一個類,自動聲明為自己類的友元。呵呵,不知道對不對。假如你想這樣定義,
bool Fruit::isSame(const string &otherName)
{
return name == otherName;
}
然後這樣調用, cout<<apple.isSame(apple2.name)<<endl;結果是通不過編譯的,道理還是不能調用一個類的私人成員。最後要說的是,我以前以為友元雖然為我們帶來了一定的方便,但是友元的破壞性也是巨大的,他破壞了類的封裝,不小心使用的話,會打擊你對C++類安全使用的信心,就像強制類型轉換一樣,能不用就不用。但是當我看了Bjarne Stroustrup 的書後,才理解了一些東西,他的意思就是友元是沒有人們說的那樣的破壞性的,因為友元的聲明權完全在類設計者手裡,他能很好控制,而不會讓友元的特性泛濫,而且在我學的更多一些後,發現友元在一些應用中必須得用到,比如一些操作符的重載,不用友元就不行,雖然個人感覺,類中成員函數省略的This形參假如沒有友元作補充支撐,根本就不敢用,因為會限制很多功能,當然有了友元就沒有關係了,可以在外面定義嘛。友元就講了這麼多,不知道是不是複雜化了。