More Effective C++——2. 操作符
操作符
條款5:對定制的 “類型轉(zhuǎn)換函數(shù)” 保持警覺
c++允許編譯器在不同類型之間執(zhí)行隱式轉(zhuǎn)換。但是部分隱式轉(zhuǎn)換存在安全性問題,例如 int–>short,
double–>char。然而,自己設(shè)計(jì)類型時(shí)可以嘗試提供特定的 “函數(shù)” 來作為隱式類型轉(zhuǎn)換使用,并保證安
全性。
[!tip]
盡管如此,最好不要提供任何類型轉(zhuǎn)換函數(shù)。
實(shí)現(xiàn)定制的類型轉(zhuǎn)換函數(shù)有兩種方案。
1. 單自變量 constructors
單自變量 constructors 指能夠以單一自變量成功調(diào)用的 constructors,也可以是擁有多個(gè)參數(shù),但除
了第一個(gè)參數(shù)之外都有默認(rèn)值。
2. 隱式類型轉(zhuǎn)換操作符
隱式類型轉(zhuǎn)換操作符,是一個(gè)擁有奇怪名稱的 member function:在關(guān)鍵詞 operator 之后加上一個(gè)類
型名稱,并且不制定返回值類型。
3. 最好不要提供任何類型轉(zhuǎn)換函數(shù)
因?yàn)樵谖搭A(yù)期的情況下,此類函數(shù)可能會被調(diào)用,而結(jié)果可能不正確,不直觀且難以調(diào)試。
a. 隱式類型轉(zhuǎn)換操作符
針對隱式類型轉(zhuǎn)換操作符,可能的情況如下:
Rational r(1, 3); std::cout << r; // 期待打印 "1/3"如果 Rational 中并為提供 operator<< 方法,但卻提供了隱式類型轉(zhuǎn)換操作符,這時(shí)就會發(fā)生意想不到
的結(jié)果。編譯器會將 Rational 轉(zhuǎn)換為 double 進(jìn)行打印,這顯然并不是期待的結(jié)果。
為了解決這個(gè)問題,應(yīng)當(dāng)使用一個(gè)函數(shù)來取代隱式類型轉(zhuǎn)換操作符,例如 asDouble。
class Rational { public:double asDouble() const; }Rational r(1, 3); std::cout << r; // 期待打印 "1/3" std::cout << r.asDouble() // 期待打印 0.333333b. 單自變量 constructors
通過單自變量的 constructor 完成的隱式轉(zhuǎn)換是很難消除的。
template<class T> class Array { public:Array(int lowBound, int highBound);Array(int size);T& operator[] (int index); };bool operator==(const Array<int>& lhs, const Array<int>& rhs);Array<int> a(10); Array<int> b(10); for (int i = 0; i < 10; ++i) {if (a == b[i]) { } // 這里寫錯(cuò)了,所以會造成額外的問題。else { } }上述代碼的一個(gè)由于寫錯(cuò),所以產(chǎn)生了一個(gè)意想不到的問題。我們撰寫這段代碼的本意是,如果 a[i] == b[i]
成立,就做一些事。然而由于寫成了 a,于是編譯器為我們選擇了我們上面寫的這段 operator==。首先,
a 可以匹配 const Array& lhs,那 b[i] 如何匹配后者呢?通過隱式類型轉(zhuǎn)換,編譯器通過 Array(int size)
的構(gòu)造方法,將 b[i] 傳入構(gòu)造出一個(gè)臨時(shí)對象,這樣就匹配成功了。
為了拒絕這種事情的發(fā)生,使用 explicit 關(guān)鍵詞,來要求該構(gòu)造函數(shù)必須顯式使用。
template<class T> class Array { public:explicit Array(int size); }; if (a == b[i]) // 非法行為 if (a == Array<int>(b[i])) // 合法行為,但是邏輯錯(cuò)誤 if (a == static_cast<Array<int>>(b[i])) // 合法,同樣邏輯錯(cuò)誤 if (a == (Array<int>)b[i]) // 合法,但是邏輯錯(cuò)誤如果你的編譯器還不支持 explicit 關(guān)鍵字,那么你可以使用 proxy class 這種方案進(jìn)行:
template<class T> class Array { public:class ArraySize {public:ArraySize(int num) : theSize(num) {}int size() const { return theSize; }private:int theSize;}explicit Array(ArraySize size); };if (a == b[i]) // 非法這種類型轉(zhuǎn)換是不能成功的,因?yàn)?num 需要首先隱式轉(zhuǎn)換為 Array::ArraySize,然后再由 ArraySize
轉(zhuǎn)換為 Array。這種連續(xù)多次轉(zhuǎn)換的行為是被編譯器禁止的。
條款6:區(qū)別 increment/decrement 操作符的 前置 和 后置 形式
為了區(qū)分 ++/-- 的前置形式和后置形式,重載函數(shù)以參數(shù)類型來區(qū)分彼此。
class UPInt { // unlimited precision int public:UPInt(int value) : _value(value) {}UPInt &operator++() { // 前置形式(*this)._value += 1;return *this;}const UPInt operator++(int) { // 后置形式UPInt oldValue = *this;++(*this);return oldValue;}UPInt &operator--() {(*this)._value -= 1;return *this;}const UPInt operator--(int) {UPInt oldValue = *this;--(*this);return oldValue;}friend ostream &operator<<(ostream &os, UPInt); private:int _value; };后置式操作符沒有使用傳入的參數(shù),只是為了區(qū)別前置式和后置式而已。為了避免發(fā)出警告,可以故意省略參
數(shù)名稱。前置式返回一個(gè) reference,而后置式返回一個(gè) const 對象。這種行為主要是為了和內(nèi)建類型保
持一致,以避免錯(cuò)誤。
除此之外,關(guān)于效率問題,顯然 后置式 通過調(diào)用 前置式 來完成任務(wù),同時(shí)還生成一個(gè)臨時(shí)對象進(jìn)行存儲。
因此,前置式效率更高。
[!tip]
選擇的原則是:除非真的需要后置式的行為(返回 const 對象),否則使用前置式。
條款7:千萬不要重載 && || 和 , 操作符
在進(jìn)行 “真假值” 判斷時(shí),c/c++ 采用“驟死式”的判斷方案,即從左到右進(jìn)行判斷,如果左面不成立則不繼
續(xù)判斷。這使得這三個(gè)操作符具有順序性,而對其進(jìn)行重載會導(dǎo)致破環(huán)這種秩序性,從而導(dǎo)致意想不到的結(jié)果。
因此應(yīng)該選擇不去重載這些操作符。
條款8:了解各種不同意義的 new 和 delete
1. new
主要需要理解 new operator 和 operator new 之間的差異。
new operator 是類似于 string *ps = new string("hello world"); 中的new。這個(gè)操作符是
由語言內(nèi)建的,不能被改變。它一般執(zhí)行兩個(gè)操作:1. 分配一個(gè)足夠防止類型對象的內(nèi)存空間。2. 調(diào)用一個(gè)
constructor,為剛才分配的內(nèi)存中那個(gè)對象設(shè)定初值。
operator new 則是用來申請內(nèi)存空間的函數(shù),通常這樣使用void *rawMemory = operator new(sizeof(string))。
此外,operator new還可以被重載,改變其行為。
需要注意的是,只要使用 new operator,必定會默認(rèn)重新使用 operator new 來分配一塊空間,并調(diào)用
constructor。通常情況下無法直接在一塊已分配的空間上直接調(diào)用 constructor。
placement new
如果需要在一些分配好的原始內(nèi)存中,直接調(diào)用 constructor 來構(gòu)建對象。有一個(gè)特殊版本的 operator
new 被成為 placement new,允許你這么做。
2. deletion and Deallocation
與 new operator 和 operator new 的關(guān)系一樣,delete operator 和 operator delete 也是
類似卻不同的兩個(gè)操作。
delete operator 與 new operator 相對應(yīng),它也執(zhí)行兩步操作:1. 調(diào)用對象的 destructor 方法,
從而釋放對象中其他引用的內(nèi)存空間。2. 執(zhí)行 operator delete 將申請的空間還給內(nèi)存。
而 operator delete 與 operator new 相對應(yīng),只進(jìn)行空間的釋放。這也就對使用者提出了新的要求,
必須手動調(diào)用對象的 destructor 方法才行。
需要注意的是,不能直接使用 delete operator 來釋放 pw 指針指向的內(nèi)容,因?yàn)樵撨^程并非使用 new
operator 來實(shí)現(xiàn)的,而是使用 placement new 來完成的,會產(chǎn)生錯(cuò)誤。
3. 數(shù)組
operator new/delete 也可以處理數(shù)組內(nèi)容,對應(yīng)的使用方法是
int main() {void *rawMemory = operator new[](sizeof(Widget) * 10);operator delete[](rawMemory); }總結(jié)
以上是生活随笔為你收集整理的More Effective C++——2. 操作符的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NanoVG 优化笔记:性能提高5倍的秘
- 下一篇: 成都瀚德科技C++高级开发工程师(win