日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

More Effective C++——2. 操作符

發布時間:2024/5/14 c/c++ 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 More Effective C++——2. 操作符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

操作符

條款5:對定制的 “類型轉換函數” 保持警覺

c++允許編譯器在不同類型之間執行隱式轉換。但是部分隱式轉換存在安全性問題,例如 int–>short,
double–>char。然而,自己設計類型時可以嘗試提供特定的 “函數” 來作為隱式類型轉換使用,并保證安
全性。

[!tip]
盡管如此,最好不要提供任何類型轉換函數。

實現定制的類型轉換函數有兩種方案。

1. 單自變量 constructors

單自變量 constructors 指能夠以單一自變量成功調用的 constructors,也可以是擁有多個參數,但除
了第一個參數之外都有默認值。

class Name { public:Name(const string &s); // 可以將 string 轉換為 Name };class Rational { public:Rational(int numerator = 0, int denominator = 1); // 可以將 int 轉換為 Rational };

2. 隱式類型轉換操作符

隱式類型轉換操作符,是一個擁有奇怪名稱的 member function:在關鍵詞 operator 之后加上一個類
型名稱,并且不制定返回值類型。

class Rational { public:operator double() const; // 將 Rational 轉換為 double }Rational r(1, 2); double d = 0.5 * r; // r 自動 轉換為 double

3. 最好不要提供任何類型轉換函數

因為在未預期的情況下,此類函數可能會被調用,而結果可能不正確,不直觀且難以調試。

a. 隱式類型轉換操作符

針對隱式類型轉換操作符,可能的情況如下:

Rational r(1, 3); std::cout << r; // 期待打印 "1/3"

如果 Rational 中并為提供 operator<< 方法,但卻提供了隱式類型轉換操作符,這時就會發生意想不到
的結果。編譯器會將 Rational 轉換為 double 進行打印,這顯然并不是期待的結果。

為了解決這個問題,應當使用一個函數來取代隱式類型轉換操作符,例如 asDouble。

class Rational { public:double asDouble() const; }Rational r(1, 3); std::cout << r; // 期待打印 "1/3" std::cout << r.asDouble() // 期待打印 0.333333

b. 單自變量 constructors

通過單自變量的 constructor 完成的隱式轉換是很難消除的。

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]) { } // 這里寫錯了,所以會造成額外的問題。else { } }

上述代碼的一個由于寫錯,所以產生了一個意想不到的問題。我們撰寫這段代碼的本意是,如果 a[i] == b[i]
成立,就做一些事。然而由于寫成了 a,于是編譯器為我們選擇了我們上面寫的這段 operator==。首先,
a 可以匹配 const Array& lhs,那 b[i] 如何匹配后者呢?通過隱式類型轉換,編譯器通過 Array(int size)
的構造方法,將 b[i] 傳入構造出一個臨時對象,這樣就匹配成功了。

為了拒絕這種事情的發生,使用 explicit 關鍵詞,來要求該構造函數必須顯式使用。

template<class T> class Array { public:explicit Array(int size); }; if (a == b[i]) // 非法行為 if (a == Array<int>(b[i])) // 合法行為,但是邏輯錯誤 if (a == static_cast<Array<int>>(b[i])) // 合法,同樣邏輯錯誤 if (a == (Array<int>)b[i]) // 合法,但是邏輯錯誤

如果你的編譯器還不支持 explicit 關鍵字,那么你可以使用 proxy class 這種方案進行:

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]) // 非法

這種類型轉換是不能成功的,因為 num 需要首先隱式轉換為 Array::ArraySize,然后再由 ArraySize
轉換為 Array。這種連續多次轉換的行為是被編譯器禁止的。

條款6:區別 increment/decrement 操作符的 前置 和 后置 形式

為了區分 ++/-- 的前置形式和后置形式,重載函數以參數類型來區分彼此。

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; };

后置式操作符沒有使用傳入的參數,只是為了區別前置式和后置式而已。為了避免發出警告,可以故意省略參
數名稱。前置式返回一個 reference,而后置式返回一個 const 對象。這種行為主要是為了和內建類型保
持一致,以避免錯誤。

UPInt upi; ++++upi; // 合理 upi++++; // 不合理

除此之外,關于效率問題,顯然 后置式 通過調用 前置式 來完成任務,同時還生成一個臨時對象進行存儲。
因此,前置式效率更高。

[!tip]
選擇的原則是:除非真的需要后置式的行為(返回 const 對象),否則使用前置式。

條款7:千萬不要重載 && || 和 , 操作符

在進行 “真假值” 判斷時,c/c++ 采用“驟死式”的判斷方案,即從左到右進行判斷,如果左面不成立則不繼
續判斷。這使得這三個操作符具有順序性,而對其進行重載會導致破環這種秩序性,從而導致意想不到的結果。

因此應該選擇不去重載這些操作符。

條款8:了解各種不同意義的 new 和 delete

1. new

主要需要理解 new operator 和 operator new 之間的差異。

new operator 是類似于 string *ps = new string("hello world"); 中的new。這個操作符是
由語言內建的,不能被改變。它一般執行兩個操作:1. 分配一個足夠防止類型對象的內存空間。2. 調用一個
constructor,為剛才分配的內存中那個對象設定初值。

operator new 則是用來申請內存空間的函數,通常這樣使用void *rawMemory = operator new(sizeof(string))。
此外,operator new還可以被重載,改變其行為。

需要注意的是,只要使用 new operator,必定會默認重新使用 operator new 來分配一塊空間,并調用
constructor。通常情況下無法直接在一塊已分配的空間上直接調用 constructor。

placement new

如果需要在一些分配好的原始內存中,直接調用 constructor 來構建對象。有一個特殊版本的 operator
new 被成為 placement new,允許你這么做。

#include <new> // 使用 placement new 需要用 <new>class Widget { public:Widget(int widgetSize) : _size(widgetSize) {}int size() {return _size;} private:int _size; };int main() {void *rawMemory = operator new(sizeof(Widget));Widget *pw = static_cast<Widget *>(rawMemory);new (pw) Widget(12); // 作為placement new 所需的第二個參數制定內存位置 }

2. deletion and Deallocation

與 new operator 和 operator new 的關系一樣,delete operator 和 operator delete 也是
類似卻不同的兩個操作。

delete operator 與 new operator 相對應,它也執行兩步操作:1. 調用對象的 destructor 方法,
從而釋放對象中其他引用的內存空間。2. 執行 operator delete 將申請的空間還給內存。

而 operator delete 與 operator new 相對應,只進行空間的釋放。這也就對使用者提出了新的要求,
必須手動調用對象的 destructor 方法才行。

int main() {void *rawMemory = operator new(sizeof(Widget));Widget *pw = static_cast<Widget *>(rawMemory);new (pw) Widget(12);pw->~Widget();operator delete(rawMemory); }

需要注意的是,不能直接使用 delete operator 來釋放 pw 指針指向的內容,因為該過程并非使用 new
operator 來實現的,而是使用 placement new 來完成的,會產生錯誤。

3. 數組

operator new/delete 也可以處理數組內容,對應的使用方法是

int main() {void *rawMemory = operator new[](sizeof(Widget) * 10);operator delete[](rawMemory); }

總結

以上是生活随笔為你收集整理的More Effective C++——2. 操作符的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。