More Effective C++讀書筆記---運算符

運算符重載,允許給予你的自定義類型有着和C++內建類型完全相樣的語法,更有甚者,它們允許你將強大的能量注入到運算符背後的函數體中。然而它也是很難駕馭的,單參數的構造函數和隱式類型轉換尤其棘手,因爲它們會被調用在沒有任何的源代碼顯示了這樣的調用的地方
五、條款5--謹慎定義類型轉換函數
1.C++編譯器能夠在兩種數據類型之間進行隱式轉換(implicit conversions),它繼承了C語言的轉換方法。這種可怕的轉換可能會導致數據的丟失
2.1所提到的是語言本身的特性,你無能爲力。不過當你定義自己的類型時,就有更多的控制力,因爲你能選擇是否提供函數讓編譯器進行隱式類型轉換
3.有兩種函數允許編譯器進行這些轉換:單參數構造函數(single-argument-constructors)和隱式類型轉換運算符
4.單參數構造函數:只定義了一個參數的函數或雖定義了多個參數但第一個參數以後的所有參數都有缺省值
5.隱式類型轉換操作符只是一個樣子奇怪的成員函數:operator關鍵字,其後跟一個類型符號。你不用定義函數的返回類型,因爲返回類型就是這個函數的名字
6.說了上面幾條,根本問題是當你在不需要使用轉換函數時,這些函數卻會被調用運行。它表明了隱式類型轉換的缺點:它們的存在將導致錯誤的發生
7.解決6的辦法是,用不使用語法關鍵字的等同的函數來替代轉換運算符(顯式調用轉換成員函數),雖然這種顯式轉換函數的使用不方便,但是函數被悄悄調用的情況不再會發生
8.通過單參數構造函數進行隱式類型轉換更難消除 解決辦法:(1)explicit關鍵字 (2)使用自定義類型來代替構造函數的單參數
六、條款6--自增(increment)、自減(decrement)操作符前綴形式與後綴形式的區別
1.句法問題:重載函數間的區別決定於它們的參數類型上的差異,但是不論是increment或decrement的前綴還是後綴都只有一個參數。爲了解決這個語言問題,C++規定後綴形式有一個int類型的參數,當函數被調用時,編譯器傳遞一個0做爲int參數的值給該函數
class UPInt
{
public:
 UPInt& operator++(); // ++前綴
 const UPInt operator++(int); // ++後綴
 UPInt& operator--()  // --前綴
 const UPInt operator--(int); // --後綴
 UPInt& operator +=(int)  // +=操作符,UPInts
};// 注意返回值類型,前綴形式返回一個引用,後綴形式返回一個const類型
2.從你開始做C程序員那天開始,你就記住increment的前綴形式有時叫做“增加然後取回”,後綴形式叫做“取回然後增加”
UPInt& UPInt::operator++()
{
 *this += 1;  // 增加
 return *this;  // 取回
}

const UPInt UPInt::operator++()
{
 UPInt oldValue = *this;  // 取回值
 ++(*this);   // 增加
 return oldValue;
}
3.後綴操作符函數沒有使用它的參數。它的參數只是用來區分前綴和後綴函數調用。如果沒有在函數裏使用參數,許多編譯器會顯示警告信息,很令人討厭。爲了避免這些警告信息,一種經常使用的方法是省略掉你不想使用的參數名稱
七、條款7--不要重載"&&","||",或","
1.與C一樣,C++使用布爾表達式短路求值法(short-circuit evaluation)。這表示一旦確定了布爾表達式的真假值,即使還有部分表達式沒有被測試,布爾表達式也停止運算
2.如果你重載&&或||,就沒有辦法提供給程序員他們所期望和使用的行爲特性,因爲你以函數調用法替代了短路求值法,極大地改變了遊戲規則
3.C++語言規範沒有定義函數參數的計算順序
4.C++的逗號表達式規則:一個包含逗號的表達式首先計算逗號左邊的表達式,然後計算逗號右邊的表達式;整個表達式的結果是逗號右邊表達式的值
5.提到逗號表達式的規則,主要是要重載,你需要模仿這個行爲特性。不幸的是你無法模仿
6.你不能重載的操作符:. .* :: ?: new delete sizeof typeid
7.你可以重載的操作符:operator new   operator delete   operator new[]    operator delete[]
+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () []
8.能重載操作這些操作符不是去重載的理由。操作符重載的目的是使程序更容易閱讀,書寫和理角,而不是用你的知識去迷惑其他人。如果沒有一個好理由重載操作符,就不要重載
八、條款8--理解各種不同含義的new和delete
1.new操作符(new operator)和new操作(operator new)
2.new操作符完成的功能分兩部分,第一部分是分配足夠的內存以便容納所需類型的對象。第二部分是它調用構造函數初始化內存中的對象。new操作符總是做這兩件事情,你不能以任何方式改變它的行爲。你所能改變的是如何爲對象分配內存。
3.new操作符調用一個函數來完成必需的內存分配,你能夠重新或重載這個函數來改變它的行爲
4.new操作符爲分配內存所調用函數的名字是operator new
5.operator new聲明:
void* operator new( size_t size );
返回值類型是void*,因爲這個函數返回一個未經處理(raw)的指針,未初始化內存。你能增加額外的參數重載函數operator new,但是第一個參數類型必須是size_t。
像malloc一樣,operator new的職責是分配內存,它對構造函數一無所知
6.有時你有一些已經被分配但是尚未處理的(raw)內存,你需要在這些內存中構造一個對象,你可以使用一個特殊的operator new,它被稱爲placement new
7.爲了使用placement new,你必須使用語句#include <new>
8.如果你想在堆上建立一個對象,應該用new操作符。它既分配內存又爲對象調用構造函數。如果你僅僅想分配內存,就應該調用operator new函數,它不會調用構造函數。如果你想定製自己的堆對象被建立時的內存分配過程,你應該寫你自己的operator new函數,然後使用new操作符,new操作符會調用你定製的operator new。如果你想在一塊已經獲得指針的內存裏建立一個對象,應該用placement new
9.爲了避免內存泄漏,每個動態內存分配必須與一個等同相反的deallocation對應。函數operator delete與delete操作符的關係與operator new與new操作符的關係一樣。

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