C++Primer學習筆記(使用類,友元,類轉換)

一、運算符重載

運算符重載和函數重載一樣,都屬於C++多態性的一種體現。其實運算符的重載有很多的例子,C++編譯器將會根據運算符以及操作數的類型來決定這個運算符到底會執行什麼操作。

這種運算符重載也是爲了能夠讓對象用的更加方便,比如我想兩個數組相加,那麼正常情況下要利用循環使得兩個數組的對應元素都相加起來,但是如果你定義了一個數組類的話,就可以使用運算符重載的特性,想計算普通加法那樣實現兩個數組對象的相加。


如果我們想 重載運算符,那麼我們就需要使用特定的重載運算符的形式。並且重載運算符也必須要重載C++中已經存在的運算符,不能自己創造一個運算符出來。

operator +()

{


}

上面這種形式就是重載了一個加法運算符。

在運算符表示法中,運算符左側的對象是調用這個運算符的對象,右邊的對象是作爲參傳遞進去的對象。

加入現在有一個TIME類

TIME a, b;

a + b ==== a.opetator+(b)

上面兩種形式的作用是相等的,都會調用類中的運算符重載函數。

另外如果將運算符重載運用到類的對象中時,不單可以實現兩個類對象相加,多個也是同樣可以的,因爲運算符一般都是從左向右結合的。


二、重載限制

運算符有一個很重要的限制就是,雖然運算符的重載可以不必放在類中做成員函數,但是重載的這個運算符的操作數中必須有一個是用戶自定義的類型。換句話說我們不能爲基本數據類型進行運算符重載。比如結構體,數組,類對象,這些都算作用戶自定義的類型。

另外,使用重載運算符的時候,使用運算符不能夠違反運算符本身的句法規則,比如不能將兩個操作數的運算符重載爲一個操作數的。並且不能修改運算符的優先級。運算符即使經過重載,優先級也是和之前的運算符相同。

並且也不是所有的運算符都可以進行重載

比如:sizeof . :: ?:這些一系列的運算符都不能夠進行重載。


運算符的重載一般可以作爲成員函數,或者是非成員函數,但是仍然有一些運算符是必須用成員函數來進行重載的。

=

()

[]

->


三、友元

我們先來看一個程序當中實際發生的問題:

比如我現在重載了一個+運算符,這個運算符是用於一個類對象和一個基本數據類型int相加的。

time a;

a = a+1 ===> a.operator+(1)

這樣調用是沒錯的。我們要時刻記住運算符的左側是調用它的對象,右邊是作爲參數傳遞進去的。

如果是這種形式:a = 1+a。按理來講這兩種形式是一樣的,但是,如果這樣調用的話會出問題,因爲運算符左側是調用的對象,如果這個函數重載爲類的一個成員函數的話,因爲int型數據不是類對象,所以不能夠調用這個運算符重載的函數。

解決辦法有兩個,第一個就是按照規矩來寫,第二個就是使用非成員函數來重載運算符,這樣一來,非成員函數使用 的任何數值 都會被顯式的作爲參數傳遞進去。

所以像這種調用

a = 1 + a; ===> a = operator+(1,a);

就可以按照這種形式來完成。雖然我們可以用這種非成員函數來暫時解決這個問題,但是一個新問題出現了,既然我們需要類的對象,計算的時候肯定需要這個對象對應的數據,那麼在非成員函數中是不允許訪問類中的私有成員的,這時候就需要了一類特殊的非成員函數,即友元函數。



四:創建和使用友元函數

創建友元函數很簡單,首先把我們要聲明的非成員函數寫在類的聲明中,並在函數的頭部加上friend

這樣的語句表達了兩個信息:

1、這個函數雖然在類中聲明,但是不屬於類的成員函數

2、這個函數雖然不是類的成員函數但是有着和成員函數一樣的權限。

聲明的時候需要使用friend關鍵字,但是在類外定義的時候是不再需要的。


在分辨一個函數是不是 這個類的友元函數的時候,就是看在她的函數代碼的實現裏面有沒有訪問到該類的私有數據成員。


五、重載<<運算符

這個運算符其實從C到C++就已經發生了重載,從原來的左移運算符到現在的有輸出功能的運算符。

一般來講,我們只能將這個運算符的輸出功能運用於cout這個ostream類的對象,之所以這個運算符在輸出上提供了很大的方面,可以不用寫格式控制符就輸出不同基本類型的數據,就是因爲在ostream類中重載了這個函數的很多版本。

前面既然提到了爲什麼<<這個運算符可以用於不同種類的基本數據類型的輸出,那麼當我們自己定義一個類的時候,如果也想利用這種cout<<對象名的形式輸出的時候,我們自己也要重載這個運算符。


如果你想把這個函數重載爲自定義類的成員函數的時候,因爲調用成員函數的時候,特別是運算符的重載函數,運算符左邊都是調用者,右邊是傳進這個函數的參數,所以如果一旦重載爲成員函數的時候,就必須使用對象名<<cout這種形式來調用,如果我們還想按照正常形式的話,就要重載爲友元函數。友元函數的返回值類型可以設置爲void.


但是,如果你看過ostream的文件的話,你就會發現重載<<運算符的函數的返回值類型是ostream對象的類型。爲什麼會這麼做,就是因爲我們平時有着這樣的用法。


int x, y;

cout<<x<<y<<endl;

由於我們說過運算符被重載爲成員函數之後,運算符左邊都是調用者,右邊都是這個函數的一個參數。所以說,我們必須保證每次使用<<來輸出基本類型數據的時候,左邊都是ostream對象。所以在調用cout<<x的時候,返回值必須是一個ostream對象才能夠繼續運行cout<<Y。


所以根據這種需求,我們在重載<<運算符的時候,必須要使用ostream對象引用的形式作爲函數的返回值。

因爲ostream對象的引用可以指向ostream對象和fstream對象,所以可以利用這種方式使得把信息輸出到文件更加方便。


六、自定義類對象的類型轉換

如果我們定義了一個對象,但是這個對象只有一個成員數據就是一個double的值。我們是不是可以這樣給對象賦值呢:

對象名=double值。道理上來講是不行的,因爲一個是基本的數據類型,另外一個是用戶自定義的數據類型,這兩個類型是不兼容的,所以編譯器不會執行自動的類型轉換,但是隻有一個參數的構造參數將會爲這種自動類型轉換增加可能。

先創建一個對象,但是並不初始化(因爲定義了一個帶有一個參數的構造函數),之後利用對象名=double值的語句爲對象賦值,此時,程序會自動調用有一個參數的構造函數,創建一個匿名的臨時對象,將double的值初始化這個對象中的那個數據成員,之後,採用對象和對象直接的成員依次賦值的方式,完成我們預期的賦值操作。這是一種隱式的類型轉換,但是這種轉換也僅僅只能夠發生在帶有一個參數的構造函數上面。但是帶有兩個參數的構造函數,完全可以把另外一個指定爲默認參數,這樣在調用的時候也可以實現把構造函數變爲類型轉換函數的功能。


有的時候這樣的特性並不是我們需要的,所以我們在函數的前面可以加上explicit關鍵字來屏蔽掉這種特性。這樣的話就關閉了隱式轉換的特性,再想轉換的時候,可以通過強制類型轉換來實現。


當類中的構造函數僅有一個參數的時候,下面的三種方式都可以用來初始化類對象:

A a = 5;

A a(5);

A a = A(5);


前面講了,可以通過單個參數的構造函數把一個基本數據類型轉化爲類對象,那麼也可以做相反的轉化,只不過不是用構造函數,而是我們要自己定義一個轉換函數來執行這個操作。


七、轉換函數的創建和使用

轉換函數是一種成員運算符函數。

轉換函數的創建形式:

operator 類型名();

這就是轉換函數的形式,但是轉換函數必須是類成員方法,並且不能有返回值和參數,所以如果想把一個類的對象轉化爲基本的數據類型,可以

operator double()把這個函數作爲類的成員函數來定義。


爲了防止這種轉換函數以及上面提到的構造函數的轉換函數 的自動執行,可以使用explict關鍵字進行聲明,也可以破壞轉換函數的定義規則,爲其加上返回值,應該儘量避免隱式調用。


八、

在重載<<運算符的時候,如果想用它來輸出用戶自定義類型的數據,與cout一起適用的話,必須重載爲一個友元函數,而且,如果想讓這個運算符的連續輸出特性用於用於自定義的類型的時候,要將返回值設置爲ostream &類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章