C++語法特性cheat paper

1. explicit關鍵字

C++中的explicit關鍵字只能用於修飾只有一個參數的類構造函數, 它的作用是表明該構造函數是顯示的, 而非隱式的, 跟它相對應的另一個關鍵字是implicit, 意思是隱藏的,類構造函數默認情況下即聲明爲implicit(隱式)。

注:C++中不存在implicit關鍵字,C#等編程語言中才有,此處僅僅是爲了說明。

上面也已經說過了, explicit關鍵字只對有一個參數的類構造函數有效, 如果類構造函數參數大於或等於兩個時, 是不會產生隱式轉換的, 所以explicit關鍵字也就無效了。

2. =delete

C++11中,對於deleted函數,編譯器會對其禁用,從而避免某些非法的函數調用或者類型轉換,從而提高代碼的安全性。

對於 C++ 的類,如果程序員沒有爲其定義特殊成員函數,那麼在需要用到某個特殊成員函數的時候,編譯器會隱式的自動生成一個默認的特殊成員函數,比如默認的構造函數、析構函數、拷貝構造函數以及拷貝賦值運算符。

爲了能夠讓程序員顯式的禁用某個函數,C++11標準引入了一個新特性:deleted函數。程序員只需在函數聲明後加上”=delete;”,就可將該函數禁用。

deleted函數特性還可用於禁用類的某些轉換構造函數,從而避免不期望的類型轉換。

deleted函數特性還可以用來禁用某些用戶自定義的類的new操作符,從而避免在自由存儲區創建類的對象。

必須在函數第一次聲明的時候將其聲明爲deleted函數,否則編譯器會報錯。即對於類的成員函數而言,deleted函數必須在類體裏(inline)定義,而不能在類體外(out-of-line)定義。

雖然defaulted函數特性規定了只有類的特殊成員函數才能被聲明爲defaulted函數,但是deleted函數特性並沒有此限制。非類的成員函數,即普通函數也可以被聲明爲deleted函數。

3. noexcept與throw

C++98 異常處理(throw)

C++98中,在函數聲明時,我們使用throw指定一個函數可以拋出異常的類型。例如:

class Ex {
public:
  double getVal();
  void display() throw();
  void setVal(int i) throw (char*, double);
 private:
   int m_val;
};

上述函數的聲明指定了該函數可以拋出異常的類型:
getVal() 可以拋出任何異常(默認);
display() 不可以拋出任何異常;
setVal() 只可以拋出char* 和 double類型異常。

從功能上來說,C++98中的異常處理機制完全能滿足我們的需要,正確的處理異常。
然而,編譯器爲了遵守C++語言標準,在編譯時,只檢查部分函數的異常規格(exception specification)。
exception specification: 函數名字後面的throw表達式,或者noexcept。

// declaration
extern void funAny(void);                   //May throw ANY exception.
void check(void) throw (std::out_of_range); // May throw only std::out_of_range.

// implementation
void check(void) throw(std::out_of_range) {
    funAny();   // Compiler does not check if
    ...         // funAny(), or one of its 
}               // subordinates, only throws std::out_of_range!

程序在運行時,如果funAny()拋出一個異常,
但是它的類型不是std::out_of_range, 異常處理機制將調用std::unexpected()(該函數自己也可能拋出異常),
這個函數默認情況下會調用std::teminate()。

C++11異常處理(noexcept)

編譯器在編譯時能過做的檢測非常有限,因此在C++11中異常聲明被簡化爲以下兩種情況:
(1)函數可以拋出任何異常(和之前的默認情況相同);
(2)函數不可以拋出任何異常。

在C++11中,聲明一個函數不可以拋出任何異常使用關鍵字noexcept。

void mightThrow(); // could throw any exceptions.
void doesNotThrow() noexcept; // does not throw any exceptions.

下面兩個函數聲明的異常規格在語義上是相同的,都表示函數不拋出任何異常。

void old_stytle() throw();
void new_style() noexcept;

它們的區別在於程序運行時的行爲和編譯器優化的結果。
使用throw(), 如果函數拋出異常,異常處理機制會進行棧回退,尋找(一個或多個)catch語句。
此時,檢測catch可以捕捉的類型,如果沒有匹配的類型,std::unexpected()會被調用。
但是std::unexpected()本身也可能拋出異常。
如果std::unexpected()拋出的異常對於當前的異常規格是有效的,
異常傳遞和棧回退會像以前那樣繼續進行。
這意味着,如果使用throw, 編譯器幾乎沒有機會做優化。
事實上,編譯器甚至會讓代碼變得更臃腫、龐大:
(1)棧必須被保存在回退表中;
(2)所有對象的析構函數必須被正確的調用(按照對象構建相反的順序析構對象);
(3)編譯器可能引入新的傳播柵欄(propagation barriers)、引入新的異常表入口,使得異常處理的代碼變得更龐大;
(4)內聯函數的異常規格(exception specification)可能無效的。

當使用noexcept時,std::teminate()函數會被立即調用,而不是調用std::unexpected();
因此,在異常處理的過程中,編譯器不會回退棧,這爲編譯器的優化提供了更大的空間。

簡而言之,如果你知道你的函數絕對不會拋出任何異常,應該使用noexcept, 而不是throw().

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