條款十九 分清成員函式,非成員函式和友元函式

2022-03-17 20:02:42 字數 2829 閱讀 9980

成員函式和非成員函式最大的區別在於成員函式可以是虛擬的而非成員函式不行。所以,如果有個函式必須進行動態繫結(見條款38),就要採用虛函式,而虛函式必定是某個類的成員函式。如果函式不必是虛擬的,情況就稍微複雜一點。

看下面表示有理數的乙個類:

class

rational ;

現在可以很容易地對有理數進行乘法操作:

rational oneeighth(1, 8

);rational onehalf(

1, 2

);rational result = onehalf * oneeighth; //

執行良好

result = result * oneeighth; //

執行良好

但不要滿足,還要支援混合型別操作,比如,rational要能和int相乘。但當寫下下面的**時,只有一半工作:

result = onehalf * 2;      //

執行良好

result = 2 * onehalf; //

出錯!

如果用下面的等價函式形式重寫上面的兩個例子,問題的原因就很明顯了:

result = onehalf.operator*(2);      //

執行良好

result = 2.operator*(onehalf); //

出錯!

物件onehalf是乙個包含operator*函式的類的例項,所以編譯器呼叫了那個函式。而整數2沒有相應的類,所以沒有operator*成員函式。

再看看那個成功的呼叫。它的第二引數是整數2,然而rational::operator*期望的引數卻是rational物件。怎麼回事?為什麼2在乙個地方可以工作而另乙個地方不行?

秘密在於隱式型別轉換。編譯器知道傳的值是int而函式需要的是rational,但它也同時知道呼叫rational的建構函式將int轉換成乙個合適的rational,所以才有上面成功的呼叫(見條款m19)。換句話說,編譯器處理這個呼叫時的情形類似下面這樣:

const rational temp(2);      //

從2產生乙個臨時

//rational物件

result = onehalf * temp; //

同onehalf.operator*(temp);

當然,只有所涉及的建構函式沒有宣告為explicit的情況下才會這樣,因為explicit建構函式不能用於隱式轉換,這正是explicit的含義。如果rational象下面這樣定義:

class

rational ;

那麼,下面的語句都不能通過編譯:

result = onehalf * 2;             //

錯誤!result = 2 * onehalf; //

錯誤!

編譯器只對函式引數表中列出的引數進行轉換,決不會對成員函式所在的物件(即,成員函式中的*this指標所對應的物件)進行轉換。

儘管如此,你可能還是想支援混合型的算術操作,而實現的方法現在應該清楚了:使operator*成為乙個非成員函式,從而允許編譯器對所有的引數執行隱式型別轉換:

const rational operator*(const rational&lhs,

const rational&rhs)

rational onefourth(

1, 4

);rational result;

result = onefourth * 2; //

工作良好

result = 2 * onefourth; //

萬歲, 它也工作了!

關於友元:

如果想過載operator>>和operator《來讀寫string物件,你會很快發現它們不能是成員函式。如果是成員函式的話,呼叫它們時就必須把string物件放在它們的左邊:

//

乙個不正確地將operator>>和

//operator《作為成員函式的類

class

string

;string

s;s >> cin; //

合法, 但

//有違常規

s << cout; //

同上

所以,如果來設計這些函式,就象這樣:

istream& operator>>(istream& input, string& string

)ostream& operator

<<(ostream&output,

const

string& string

)

上面兩個函式都要訪問string類的data成員,而這個成員是私有(private)的。但我們已經知道,這個函式一定要是非成員函式。這樣,就別無選擇了:需要訪問非公有成員的非成員函式只能是類的友元函式。

結論:·虛函式必須是成員函式。如果f必須是虛函式,就讓它成為c的成員函式。

·operator>>和operator《決不能是成員函式。如果f是operator>>或operator<<,讓f成為非成員函式。如果f還需要訪問c的非公有成員,讓f成為c的友元函式。

編譯器只對函式引數表中列出的引數進行轉換,決不會對成員函式所在的物件(即,成員函式中的*this指標所對應的物件)進行轉換。

·其它情況下都宣告為成員函式。如果以上情況都不是,讓f成為c的成員函式。

分清成員函式,非成員函式和友元函式

成員函式和非成員函式最大的區別在於成員函式可以是虛擬的而非成員函式不行。所以,如果有個函式必須進行動態繫結 見條款38 就要採用虛函式,而虛函式必定是某個類的成員函式。關於這一點就這麼簡單。如果函式不必是虛擬的,情況就稍微複雜一點。看下面表示有理數的乙個類 class rational 這是乙個沒有...

分清成員函式,非成員函式和友元函式

成員函式和非成員函式最大的區別在於成員函式可以是虛擬的而非成員函式不行。所以,如果有個函式必須進行動態繫結 見條款38 就要採用虛函式,而虛函式必定是某個類的成員函式。關於這一點就這麼簡單。如果函式不必是虛擬的,情況就稍微複雜一點。看下面表示有理數的乙個類 class rational 這是乙個沒有...

成員函式 非成員函式和友元函式

成員函式和非成員函式最大的區別在於成員函式可以是虛擬的而非成員函式不行。所以,如果有個函式必須進行動態繫結,就要採用虛函式,而虛函式必定是某個類的成員函式。如果函式不必是虛擬的,情況就稍微複雜一點。看下面表示有理數的乙個類 class rational 這是乙個沒有一點用處的類 介面最小,但遠不夠完...