[轉載] C++11新特性

C++11標準發佈已有一段時間了, 維基百科上有對C++11新標準的變化和C++11新特性介紹的文章. 我是一名C++程序員,很是想了解一下C++11. 英文版的維基百科看起來很是費勁,而中文版維基百科不是知道是臺灣仍是香港人翻譯的而後由工具轉換成簡體中文的,有些術語和語言習慣和大陸程序不同! 我決定結合這兩個版本按照我本身的習慣把這篇文章整理到個人博客中.分享給關注我和關注C++11的朋友們. 固然了, 本人水平有限,英語水平也很通常,就把這個過程當作學習C++11的過程吧.文章中確定會有不少錯誤或描述不恰當的地方. 很是但願看到的朋友能給我指出來.html

如下是關於C++11的英文版本和中文版本維基百科的連接:java

http://en.wikipedia.org/wiki/C++11c++

http://zh.wikipedia.org/wiki/C++0x程序員

   目錄

   0.簡介正則表達式

C++11,以前被稱做C++0x,即ISO/IEC 14882:2011,是目前的C++編程語言的正式標準。它取代第二版標準ISO/IEC 14882:2003(初版ISO/IEC 14882:1998發佈於1998年,第二版於2003年發佈,分別通稱C++98以及C++03,二者差別很小)。新的標準包含了幾個核心語言增長的新特性,並且擴展C++標準程序庫,併入了大部分的C++ Technical Report 1程序庫(數學的特殊函數除外)。最新的消息被公佈在 ISO C++ 委員會網站(英文)。算法

ISO/IEC JTC1/SC22/WG21 C++ 標準委員會計劃在2010年8月以前完成對最終委員會草案的投票,以及於2011年3月召開的標準會議完成國際標準的最終草案。然而,WG21預期ISO將要花費六個月到一年的時間才能正式發佈新的C++標準。爲了可以如期完成,委員會決定致力於直至2006年爲止的提案,忽略新的提案。最終,於2011年8月12日公佈,並於2011年9月出版。2012年2月28日的國際標準草案(N3376)是最接近於現行標準的草案,差別僅有編輯上的修正。express

像C++這樣的編程語言,經過一種演化的過程來發展其定義。這個過程不可避免地將引起與現有代碼的兼容問題。不過根據Bjarne Stroustrup(C++的創始人,標準委員會的一員)表示,新的標準將幾乎100%兼容現有標準。編程

 

1.來自以往版本標準的變動(候選變動)數組

C++的修訂範圍包括核心語言以及標準程序庫。在開發2011版標準的各個特性的過程當中,標準委員會堅持如下指導思想:
    * 維持與C++98,可能的話還有C之間的兼容性與穩定性;
    * 儘量經過經過標準程序庫來引進新的特性, 而不是擴展核心語言;
    * 可以促進編程技術的變動優先;
    * 改進 C++ 以幫助系統和程序庫的設計,而不是引進只對特定應用有用的新特性;
    * 加強類型安全,給現行不安全的技術提供更安全的替代方案;
    * 加強直接與硬件協同工做的性能和能力;
    * 爲現實世界中的問題提供適當的解決方案;
    * 實行零負擔原則(若是某些功能要求的額外支持,那麼只有在該功能被用到時這些額外的支持才被用到);
    * 使C++易於教學promise

注重對初學者的關注,由於他們構成了計算機程序員的主體。也由於許多初學者不肯擴展他們的C++知識,他們僅限於掌握C++中本身所專精的部分. 

 

2.核心語言的擴充

C++標準委員會的一個職責是開發語言核心. 核心語言被大幅改進的領域包括: 多線程支持, 泛型編程支持, 統一的初始化和提升性能.
    這篇文章將核心語言的特性和變化大體分爲: 提升運行期性能, 提升編譯期性能, 加強可用性, 和新特性4大類. 某些特性能夠被劃分到多個分類中, 但只會在主要體現該特性的分類中討論一次.

 

3.提升核心語言運行性能
    如下特性主要是爲了提升性能提或計算速度等設計的.

3.1 右值引用和移動構造
    在C++03及以前的標準中,臨時對象(稱爲右值"R-values",由於一般位於賦值運算符的右邊)的值是不能改變的,和C語言同樣, 且沒法和 const T& 類型作出區分。儘管在某些狀況下臨時對象的確會被改變,甚至有時還被視爲是一個有用的漏洞。C++11新增長了一個很是量引用(non-const reference)類型,稱做右值引用(R-value reference),標記爲T &&。右值引用所引用的臨時對象能夠在該臨時對象被初始化以後作修改,這是爲了容許 move 語義。
    C++03 性能上被長期詬病的問題之一,就是其耗時且沒必要要的深拷貝。深拷貝會隱式地發生在以傳值的方式傳遞對象的時候。例如,std::vector內部封裝了一個C風格的數組和其元素個數,若是建立或是從函數返回一個std::vector的臨時對象,要將其保存起來只能經過生成新的std::vector對象而且把該臨時對象全部的數據複製進去。該臨時對象和其擁有的內存會被銷燬。(爲簡單起見,這裏忽略了編譯器的返回值優化)
    在 C++11中,std::vector有一個"移動構造函數",對某個vector的右值引用能夠單純地從右值複製其內部C風格數組的指針到新的vector中,而後將右值中的指針置空。由於這個臨時對象不會再被使用,沒代碼會再訪問這個空指針, 並且由於這個臨時對象的內部指針是NULL,因此當這個臨時對象離開做用域時它的內存也不會被釋放掉.因此,這個操做不只沒有代價高昂的深拷貝, 仍是安全的,對用戶不可見的!

這個操做不須要數組的複製,並且空的臨時對象的析構也不會銷燬內存。返回vector臨時對象的函數只須要返回std::vector<T>&&。若是vector沒有move 構造函數,那麼就會調用常規拷貝構造函數。若是有,那麼就會優先調用move構造函數,這可以避免大量的內存分配和內存拷貝操做。

右值引用不用對標準庫以外的代碼作任何改動就能夠爲已有代碼帶來性能上的提高. 返回值類型爲std::vector<T>的函數返回了一個std::vector<T>類型的臨時對象,爲了使用移動構造不須要顯示地將返回值類型改成std::vector<T>&&, 由於這樣的臨時對象會被自動看成右值引用. 可是在c++03中, std::vector<T>沒有移動構造函數, 帶有const std::vector<T>& 參數的拷貝構造會被調用, 這會致使大量內存分配和拷貝動做.
出於安全考慮, 須要施加一些限制! 一個已命名的變量即便聲明爲右值,也不會被視爲右值.想要獲取一個右值,應該使用模板函數std::move<T>(). 右值引用也能夠在特定狀況下被修改, 主要是爲了與移動構造函數一塊兒使用!
因爲"右值引用"這個詞的天然語義,以及對"左值引用"(常規引用)這個詞的修正, 右值引用可讓開發者提供完美的函數轉發! 與可變參數模板結合時, 這個能力讓模板函數可以完美地將參數轉發給帶有這些參數的另外一個函數.這對構造函數的參數轉發最爲有用,建立一個可以根據特定的參數自動調用適當的構造函數的工廠函數.

3.2 泛化的常數表達式

  C++一直以來都有常量表達式的概念.這種表達式就像3+4這種在編譯期和運行時都能獲得相同結果的表達式. 常量表達式給編譯器提供了優化的機會, 編譯器計算出他們的值並把結果硬編碼到程序中. 而且C++規格文檔中有不少地方要求使用常量表達式. 例如,定義一個數組須要常量表達式(來指定數組大小), 枚舉值必須是常量表達式.
      然而,常量表達式中歷來都不容許調用函數或建立對象. 因此,像下面這樣的簡單代碼倒是非法的:

int get_five() {return 5;} 
int some_value[get_five() + 7]; // 建立一個包含12個整數的數組. 這種形式在C++中是非法的.

  這在C++03中是非法的, 由於get_five() + 7不是常量表達式. C++03的編譯器在編譯期沒辦法知道get_five()是常量.由於從理論上講, 這個函數能夠影響(改變)一個全局變量或調用其它非運行時常量函數等.
     C++11引入了constexpr關鍵字, 容許用戶去保證一個函數或對象的構造函數是一個編譯期常量.上面的例子能夠寫成下面這樣:

constexpr int get_five() {return 5;} 
int some_value[get_five() + 7]; // 建立一個包含12個整數的數組. 這種形式在C++11中是合法的.

  這樣可讓編譯器理解並驗證get_five()是一個編譯期常量!做用在函數上的constexpr關鍵字對函數的行爲施加了一些限制. 首先, 這個函數的返回值類型不能是void; 其次, 在函數體中不能聲明變量或新類型; 第三, 函數體內只能包含聲明語句,空語句和單個return語句且,return語句中的表達式也必須是常量表達式.
  在c++11以前, 變量的值只有在變量被聲明爲const類型,有常量表達式初始化它, 而且是整型或枚舉類型時才能用在常量表達式中. C++11去掉必須是整數或枚舉的限制,若是定義變量時用了constexpr關鍵字:

constexpr double earth_gravitational_acceleration = 9.8;
constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0;

  這種數據變量是隱式的常量, 必須用常量表達式初始化.想要構造用戶定義類型的常量值,構造函數也必須用constexpr聲明.

    3.3 對POD定義的修正                                                        在C++03中, 類或結構體必須遵照幾條規則才能認爲是POD類型.符合這種定義的類型可以產生與C兼容的對象(內存)佈局, 並且能夠被靜態初始化.C++03標準對與C兼容或能夠靜態初始化的類型有嚴格的限制,儘管不是因技術緣由致使編譯器不接受這樣的編碼.若是建立了一個C++03的POD類型,又想要增長一個非虛成員函數, 那麼這個類型就再也不是POD類型了, 不能靜態初始化,而且與C不兼容了, 儘管沒有改變內存佈局.

     C++11將POD的概念拆分紅了兩個獨立的概念:平凡的(trivial)和標準佈局(standard-layout), 放寬了幾條POD規則. 一個平凡的(trivial)類型能夠靜態初始化. 這意味着能夠經過memcpy來拷貝數據,而沒必要經過拷貝構造函數. 平凡類型的變量的生命期是從分配存儲空間開始的,而不是從構造完成開始.

    平凡的類和結構體定義以下:

      1 有一個平凡的默認構造函數. 這可使用默認構造函數語法, 例如SomeConstructor() = default;

    2 有平凡的拷貝和移動構造函數, 可使用默認語法.

    3 有平凡的拷貝和移動賦值運算符, 可使用轉變語法.

    4 有一個平凡的析構函數, 必須是非虛函數.

   只有當一個類沒有虛函數,沒虛基類時它的構造函數纔是平凡的. 拷貝和移動操做還要求類的全部非靜態數據成員都是平凡的.

   一個類型是標準佈局的,就意味着它將以與C兼容的方式來排列和打包它的成員.標準佈局的類和結構體定義以下:

    1 沒有虛函數

    2 沒有虛基類

    3 全部的非靜態數據成員都有相同的訪問控制 (public, private, protected)

    4 全部的非靜態數據成員, 包括基類中的, 都要在繼承體系中的同一個類中.

    5 以上規則也適用於類體系中的全部基類和全部非靜態數據成員.

    6 沒有和第一個定義的靜態數據成員相同類型的基類

    若是一個類型是平凡的(trivial),是標準佈局的, 而且全部的非靜態數據成員和基類都是POD類型的, 那麼這個類型就是POD類型.                                                                                                                                                                        

  4 核心語言建構期性能提升

   4.1 外部模板                                                                在C++03中,只要在編譯單元內遇到被完整定義的模板,編譯器都必須將其實例化(instantiate)。這會大大增長編譯時間,特別是模板在許多編譯單元內使用相同的參數實例化。沒有辦法告訴C++不要引起模板的實例化.C++11引入了外部模板聲明, 就像外部數據聲明同樣. C++03用下面的語法迫使編譯器實例化一個模板:

    template class std::vector<MyClass>;
    而C++11提供下面的語法告訴編譯器在當前編譯單元中不要實例化這個模板:

    extern template class std::vector<MyClass>;

  5 核心語言可用性的加強

  這些特性存在的主要目的是爲了讓C++更使用. 這些特性能夠改進類型安全, 最小化代碼重複, 儘量減小錯誤代碼等.                                                                                                                                      

  5.1 初始化列表

  C++03從C語言繼承了初始化列表這一特性. 在一對大括號中列出參數的方式來給出一個結構體或者數組, 這些參數值按照各個成員在結構體中的定義順序來排列.這些初始化列表是遞歸的,因此一個結構體數組或包含另外一個結構體的結構體可使用它們.

[cpp]  view plain copy
 
  1. struct Object {  
  2. float first;  
  3. int second;  
  4. };  
  5.   
  6. Object scalar = {0.43f, 10}; //One Object, with first=0.43f and second=10  
  7. Object anArray[] = {{13.4f, 3}, {43.28f, 29}, {5.934f, 17}}; //An array of three Objects  

 

   這對於靜態初列表或者只想把結構體初始化爲某個特定值而言是很是有用的. C++提供了構造函數來初始化對象, 但這沒有初始化列表方便.C++03只容許符合POD定義的類型使用初始化列表,非POD的類型不能使用,就連至關有用的STL容器std::vector也不行.C++11擴展了初始化列表, 使用它能夠用在全部類上,包括像vector這樣的標準容器.
   C++11把初始化列表的概念綁到一個叫作std::initializer_list的模板上.這容許構造函數或其餘函數將初始化列表作爲參數.例如:

[cpp]  view plain copy
 
  1. class SequenceClass {  
  2. public:  
  3. SequenceClass(std::initializer_list list);  
  4. };  

 

  這使得能夠從一串整數來建立SequenceClass對象, 例如:
      SequenceClass some_var = {1, 4, 5, 6};
      這個構造函數是一種特殊的構造函數,叫作初始化列表構造函數(initializer-list-constructor).有這種構造函數的類在統一初始化中會被特殊對待(詳見5.2)
類型std::initializer_list<>是個第一級的C++11標準程序庫類型. 可是,它們只能由C++11經過{}語法來靜態構造!這個列表一經構造即可複製,雖然這只是copy-by-reference.初始化列表是常數;一旦被建立,其成員均不能被改變,成員中的數據也不可以被改變.
  由於初始化列表是真實類型,除了構造函數以外還可以被用在其餘地方。常規的函數可以使用初始化列表做爲參數。例如: 

[cpp]  view plain copy
 
  1.    void FunctionName(std::initializer_list list);  
  2.  FunctionName({1.0f, -3.45f, -0.4f});  
  3.  標準容器也可以以這種方式初始化:  
  4. std::vector v = { "xyzzy", "plugh", "abracadabra" };  
  5. std::vector v({ "xyzzy", "plugh", "abracadabra" });  
  6. std::vector v{ "xyzzy", "plugh", "abracadabra" }; // 參見下面 "統一的初始化"<span style="font-family: verdana, 'ms song', Arial, Helvetica, sans-serif;"> </span>  

 

  5.2 統一的初始化

  C+03在初始化類型方面有着許多問題.初始化類型有數種方法,並且交換使用時不會都產生相同結果。傳統的建構式語法,看起來像是函數聲明,並且必須採起一些步驟保證不破壞編譯器那些最讓人惱火的解析規則.只有聚合體和POD類型可以用集合式初始化(經過SomeType var = {}; 形式的語法)
    C++11提供了一個徹底統一的能夠用在任何類型的對象的初始化語法. 它擴展了初始化列表語法:

[cpp]  view plain copy
 
  1. struct BasicStruct {  
  2.    int x;  
  3.    double y;  
  4.   };  
  5.     
  6.   struct AltStruct {  
  7.    AltStruct(int x, double y) : x_{x}, y_{y} {}  
  8.     
  9.   private:  
  10.    int x_;  
  11.    double y_;  
  12.   };  
  13.     
  14.   BasicStruct var1{5, 3.2};  
  15.   AltStruct var2{2, 4.3};  

 

  var1初始化行爲就像聚合初始化同樣.也就是說,每一個數據成員就是一個對對象, 按順序從初始化列表中拷貝一個對應的值來初始化它們.若是有須要, 會進行隱式類型轉換.若是存在向下類型轉換(轉換後的數據類型不能表示原數據類型,轉換後可能有數據丟失,例如將unsigned轉換成int), 那麼這個程序就是病態的,會致使編譯失敗. var2的初始化則是簡單地調用構造函數.
  統一的初始化還可作下面這件事:

[cpp]  view plain copy
 
  1. struct IdString   
  2. {  
  3.    std::string name;  
  4.    int identifier;  
  5. };  
  6.     
  7. IdString get_string()   
  8. {  
  9.    return {"foo", 42}; //注意,這裏沒有指定具體類型.  
  10. }  

 

  統一初始化不會取代構造函數語法,仍是有一些時候是須要構造函數語法的.若是一個類有初始化列表構造函數(TypeName(initializer_list);),假定它有資格成爲構造函數之一(咱們知道,一個類能夠有多個構造函數),那麼它的優先級會高於其它形式的構造函數.C++11版本的std::vector就有一個初始化列表構造函數.這意味着  std::vector the_vec{4};會調用初始化列表構造函數,而不是調用以vector大小爲惟一參數的構造函數. 要訪問後一個構造函數, 用戶必須直接使用標準構造函數語法.

  5.3 類型推導

  在C++03(還有C)中,必須顯式指定變量的類型.然而,隨着模板類型和模板元編程技術的出現,某些東西的類型,尤爲是函數的返回類型,可能不是那麼容易表示的了. 在這種狀況下,將中間結果存儲在某個變量中是件很困難的事情.可能須要去了解特定的模板元編程庫的內部實現.
     C++11提供兩種方法來緩解上述問題. 一,定義有顯式初始化的變量能夠用auto關鍵字來自動肯定變量類型,這將用初始化表達式的類型來建立變量:

[cpp]  view plain copy
 
  1. auto some_strange_callable_type = boost::bind(&some_function, _2, _1, some_object);  
  2. auto other_variable = 5;  

 

  some_strange_callable_type的類型很簡單, 就是boost::bind模板函數返回值的類型.做爲編譯器語義分析責任的一部份,編譯很容易肯定這個類型,但程序員就沒那麼容易肯定了.otherVariable 的類型一樣也是定義明確的,程序員很容易就能判別。它是個int(整數),就和整數字面值的類型同樣。

  另外,關鍵字decltype能夠用來在編譯期肯定表達式的類型.例如:

[cpp]  view plain copy
 
  1. int some_int;  
  2. decltype(some_int) other_integer_variable = 5;  

 

  decltype 和 auto 一塊兒使用會更爲有用,由於 auto 參數的類型只有編譯器知道.然而 decltype對於那些大量運用運算符重載和類型特化來編碼的表達式很是有用。auto對減小代碼冗餘也頗有用.好比說, 程序員不用像下面這樣寫代碼:

[cpp]  view plain copy
 
  1. for (std::vector::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr)  
  2. //而能夠用更簡短的形式:  
  3. for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr)  

 

  這兩種形式的差距會隨着你使用的容器的嵌套層次而增長, 這種狀況下typedef也是一種減小代碼的好方法!由decltype得出的類型能夠和由auto推導出的類型不一樣:

[cpp]  view plain copy
 
  1. #include<vector>  
  2. int main() {  
  3. const std::vector v(1);  
  4. auto a = v[0];        // a 是 int 類型  
  5. decltype(v[1]) b = 1; // b 是 const int& 類型, 是std::vector::operator[](size_type) const  
  6.                       // 的返回類型  
  7. auto c = 0;           // c 是 int 類型  
  8. auto d = c;           // d 是 int 類型  
  9. decltype(c) e;        // e 是 int 類型, c變量的類型  
  10. decltype((c)) f = c;  // f 是int&類型, 由於(c)是一個左值  
  11. decltype(0) g;        // g 是 int 類型, 由於0是一個右值  

 

  5.4 基於範圍的for循環

  在C++03中,要遍歷一個list中的元素須要不少代碼.其它語言實現支持"糖塊句法",容許程序經過一個簡單的"foreach"語句自動遍歷list中的元素.其中之一就是Java語言, 它從5.0開始支持加強的for循環.
     C++11增長了一個相似的特性, for語句能夠簡單地遍歷列表中的元素.

[cpp]  view plain copy
 
  1. int my_array[5] = {1, 2, 3, 4, 5};  
  2. // double the value of each element in my_array:  
  3. for (int &x : my_array)   
  4. {  
  5.     x *= 2;  
  6. }  

 

  這種形式的for語句叫做"基於範圍的for語句",它會遍歷列表中的每個元素.能夠用在C風格數組,初始化列表和那些帶有能返回迭代器的begin()和end()函數的類型上.全部提供了begin/end的標準容器均可以使用基於範圍的for語句.

  5.5 Lamda函數與表達式

  C++11提供了建立匿名函數的能力,叫作Lamda函數. 具體內容請參考: http://www.cnblogs.com/pzhfei/archive/2013/01/14/lambda_expression.html

       5.6 一種新的函數語法

  標準C的函數聲明語法對C語言的特性集而言徹底足夠了. 由於C++從C發展而來, 保留了C的基本語法並在須要的地方進行了擴展. 然而,C++的結構變得更加複雜了,暴露出了不少的侷限性,尤爲是模板函數的聲明.下面的例子在C++03中是不容許的:

[cpp]  view plain copy
 
  1. template  
  2. Ret adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Ret must be the type of lhs+rhs  

 

  Ret的類型是lhs+rhs的結果的類型.就算用前面提到的C++11中的decltype,也是不行的:

[cpp]  view plain copy
 
  1. template  
  2. decltype(lhs+rhs) adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;} //Not legal C++11  

 

  這不是合法的C++,由於lhs和rhs還沒定義;解析器解析完函數原型的剩餘部分以前,它們還不是有效的標識符.
  爲此, C++11引入了一種新的函數聲明語法,叫作後置返回類型(trailing-return-type).

[cpp]  view plain copy
 
  1. template  
  2. auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}  

 

  這種語法能夠用到更普通的函數聲明和定義上:

[cpp]  view plain copy
 
  1.  struct SomeStruct  {  
  2.      auto func_name(int x, int y) -> int;  
  3.  };  
  4.   
  5. auto SomeStruct::func_name(int x, int y) -> int {  
  6. return x + y;  
  7. }  

 

  關鍵字auto的這種用法與在自動類型推導中有所不一樣.

5.7 對象構造的改進

C++03中類的構造函數不容許調用該類的其它構造函數;每一個構造函數都必須本身或者調用一個公共的成員函數來構造類的所有成員.例如:

[cpp]  view plain copy
 
  1. class SomeType  {  
  2.     int number;  
  3.    
  4. public:  
  5.     SomeType(int new_number) : number(new_number) {}  
  6.     SomeType() : number(42) {}  
  7. };  
  8. class SomeType  {  
  9.     int number;  
  10.    
  11. private:  
  12.     void Construct(int new_number) { number = new_number; }  
  13. public:  
  14.     SomeType(int new_number) { Construct(new_number); }  
  15.     SomeType() { Construct(42); }  
  16. };  

 

  並且,基類的構造函數不能直接暴露給派生類;每一個派生類必須實現本身的構造函數哪怕基類的構造函數已經夠用了.非靜態數據成員不能在聲明的地方初始化.它們只能在構造函數中初始化.

  C++11爲這些問題提供瞭解決方案.C++11容許構造函數調用另外一個構造函數(叫作委託構造).這容許構造函數利用其它構造函數的行爲而只需增長少許的代碼.C#,java和D語言都提供了這種功能. C++的語法以下:

[cpp]  view plain copy
 
  1. class SomeType  {  
  2.     int number;  
  3.    
  4. public:  
  5.     SomeType(int new_number) : number(new_number) {}  
  6.     SomeType() : SomeType(42) {}  
  7. };  

 

  注意:這個例子能夠經過給new_number設定一個默認參數來達到相同的效果.可是,這種新語法可讓這個默認值在實現中來設置而不是在接口中設置.這帶來的一個好處就是,對庫代碼的維護者而言,在接口中(頭文件中)聲明默認值,這個默認值被嵌入到了調用端;要改變這個默認值的話,調用端的代碼都須要從新編譯.但委託構造能夠在實現中(CPP文件中)來改變這個默認值, 這樣調用端的代碼就不須要從新編譯,只用從新編譯這個庫就能夠了.
    還有一個附加說明: C++03認爲,構造函數執行完了一個對象就被構造好了. 而C++11則認爲,只要任何一個構造函數執行完了,對象就算構造完成了. 因爲可能有多構造函數會被執行,C++11的這種作法就意味着,全部的委託構造函數都將在一個已經用它本身類型徹底構造好的對象上執行.這句話什麼意思呢?舉個例子, B 繼承自 A, B的構造函數裏調用了A的構造函數;當A的構造函數執行完之後,就已經有一個A類的對象構造完成了.而這時B的構造函數不會再構造一個新對象,而是把那個A對象改形成B類的對象(這是個人推測).再舉一個例子,類C有兩個構造函數C1和C2, C2調用了C1. 當C1執行完後,已構造好了一個C類對象.而這時C2的代碼會直接做用在這個對象上,不會再構造一個新對象.C++03就會構造2個對象,其中一個是臨時對象.
    對於基類的構造函數,C++11容許一個類指定要不要繼承基類的構造函數.注意,這是一個"所有"或"全不"的特性,要麼繼承基類的所有構造函數,要麼一個都不繼承.
    此外,對多重繼承有一些限制,從多個基類繼承而來的構造函數不能夠有相同的函數簽名(signature).而派生類的新加入的構造函數也不能夠和繼承而來的基類構造函數有相同的函數簽名,由於這至關於重複聲明.語法以下:

[cpp]  view plain copy
 
  1. class BaseClass {  
  2. public:  
  3.     BaseClass(int value);  
  4. };  
  5.    
  6. class DerivedClass : public BaseClass {  
  7. public:  
  8.     using BaseClass::BaseClass;  
  9. };  
  10. 對於成員初始化,C++11容許下面這樣的語法:  
  11. class SomeClass {  
  12. public:  
  13.     SomeClass() {}  
  14.     explicit SomeClass(int new_value) : value(new_value) {}  
  15.    
  16. private:  
  17.     int value = 5;  
  18. };  

 

  每個構造函數都將把value初始化爲5, 若是它們沒用其它值來覆蓋這個初始化的話.上面那個空的構造函數會把value初始化爲類定義時的狀態5.而那帶有參數的構造函數會用指定的值來初始化value.成員的初始化也可使用前面提到的統一初始化.

5.8 顯式重寫(覆蓋,override)和final   

在C++03中,很容易讓你在本想重寫基類某個函數的時候卻意外地建立了另外一個虛函數.例如:

[cpp]  view plain copy
 
  1. struct Base {  
  2.     virtual void some_func(float);  
  3. };  
  4.    
  5. struct Derived : Base {  
  6.     virtual void some_func(int);  
  7. };  

 

  原本Derived::some_func函數是想替代Base中那個函數的.可是因它的接口不一樣, 又建立了一個虛函數.這是個常見的問題, 特別是當用戶想要修改基類的時候.
  C++11引入了新的語法來解決這個問題:

[cpp]  view plain copy
 
  1. struct Base {  
  2.     virtual void some_func(float);  
  3. };  
  4.    
  5. struct Derived : Base {  
  6.     virtual void some_func(int) override; // 病態的,不會重寫基類的方法  
  7. };  

 

  override 這個特殊的標識符意味編譯器將去檢查基類中有沒有一個具備相同簽名的虛函數,若是沒有,編譯器就會報錯!
  C++11還增長了防止基類被繼承和防止子類重寫函數的能力.這是由特殊的標識符final來完成的,例如:

[cpp]  view plain copy
 
  1. struct Base1 final { };  
  2.    
  3. struct Derived1 : Base1 { }; // 病態的, 由於類Base1被標記爲final了  
  4.    
  5. struct Base2 {  
  6.     virtual void f() final;  
  7. };  
  8.    
  9. struct Derived2 : Base2 {  
  10.     void f(); // 病態的, 由於虛函數Base2::f 被標記爲final了.  
  11. };  

 

  在這個例子中, virtual void f() final;語句聲明瞭一個虛函數卻也阻止了子類重寫這個函數.它還有一個做用,就是防止了子類將那個特殊的函數名與新的參數組合在一塊兒.
  須要注意的是,override和final都不是C++語言的關鍵字.他們是技術上的標識符,只有在它們被用在上面這些特定的上下文在纔有特殊意義.用在其它地方他們仍然是有效標識符.

5.9 空指針常量

  本節中出的"0"都將解釋爲"一個求值結果爲0的int型常量表達式". 實際上任何整數類型均可以做爲常量表達式.
  自從1972年C誕生以來,常量0就有着int型常量和空指針的雙重角色.C語言用預處理宏NULL來處理這個固有的歧義, NULL一般被定義爲(void*)0或0.而C++不採用一樣行爲,只容許0作空指針常量.而這與函數重載配合時就顯得有些弱智了.

[cpp]  view plain copy
 
  1. void foo(char *);  
  2. void foo(int);  

 

  若是NULL定義爲0,那麼foo(NULL);語句將會調用foo(int).這幾乎一定不是程序員想要的,也不是代碼直觀上要表達的意圖.
     C++11經過引入一個新的關鍵字nullptr充當單獨的空指針常量來糾正這個問題.它的類型是nullptr_t,是一個能夠隱式轉換任意類型的指針或指向成員的指針的類型,而且能夠和這些類型進行比較.它不能隱式轉換爲整型,也不能與整型作比較,bool類型除外.儘管最初的提議中一個nullptr類型的右值不該該能轉換爲bool類型,可是爲了保持與常規指針類型的一致性,核心語言工做組仍是認定這種轉換是合理的. 爲了向下兼容,0仍然是一個有效的空指針常量!

[cpp]  view plain copy
 
  1. char *pc = nullptr;     // OK  
  2. int  *pi = nullptr;     // OK  
  3. bool   b = nullptr;     // OK. b is false.  
  4. int    i = nullptr;     // error  
  5.    
  6. foo(nullptr);           // calls foo(char *), not foo(int);<span style="rgb(255, 255, 255); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif;"> </span>  

 

5.10 強類型枚舉

  在C++03中,枚舉不是類型安全的.他們其實是整數,儘管他們是不一樣的枚舉類型.這使得咱們能夠比較兩種不一樣類型的枚舉值.C++03提供的惟一安全性就是,一個整數或一個枚舉類型的值不能隱式地轉換成另外一個枚舉類型.此外,底層的具體的整數類型(short,long,int,...)是由實現(編譯器)定義的,標準並沒有明確規定.所以,那些枚舉變量的大小的代碼將是不可移植的.最後,枚舉值是暴露在外層做用域(直接包含枚舉定義的做用域)中的.因此,兩個不一樣枚舉類型的成員不可能有相同的名字.
  C++11引入了一個沒上述問題的特殊"枚舉類".使用 enum class(也能夠用同義詞enum struct)來聲明:

[cpp]  view plain copy
 
  1. enum class Enumeration {  
  2.     Val1,  
  3.     Val2,  
  4.     Val3 = 100,  
  5.     Val4 // = 101  
  6. };  

 

  這種枚舉是類型安全的;枚舉值不能隱式地轉換成整數,因此也不能夠和整數作比較.表達式 Enumeration::Val4 == 101會報一個編譯錯誤.
枚舉類的底層類型老是已知的.默認是int型,這能夠用其它整數類型來覆蓋它.就像下面這個例子:

[cpp]  view plain copy
 
  1. enum class Enum2 : unsigned int {Val1, Val2};  

 

  老式的枚舉被放在直接包含該定義的做用域中.新式的枚舉被放在枚舉類的做用中.因此,上例中Val1是未定義的,而Enum2::Val1是已定義的.
  C++11還提供了一個過渡語法讓老式的枚舉類型能夠提供顯式的做用域和定義底層整數類型.語法以下:

[cpp]  view plain copy
 
  1. enum Enum3 : unsigned long {Val1 = 1, Val2};  

 

  這個例子中枚舉名字被定義在枚舉類型的做用域內(Enum3::Val1),可是爲了向下兼容它們也會被放在直接包含在Enum3所在的做用域中.

5.11 右尖括號

C++03的解析器都把">>"定義爲右移運算符.可是,在嵌套的模板聲明中,程序員每每傾向於忽略兩個右尖括號之間的空格.這會致使編譯器報一個語法錯誤.
C++11改進了編譯器的解析規則,儘量地將多個右尖括號(>)解析成模板參數結束符.能夠用圓括號來改變這個規則,圓號的優先級比它高.例如:

[cpp]  view plain copy
 
  1. template<bool Test> class SomeType;  
  2. std::vector<SomeType<1>2>> x1;  // 被解析成 std::vector of SomeType<true> 2>,  
  3. // 這是錯誤的語法, 1 被當成 true 了.  
  4. std::vector<SomeType<(1>2)>> x1;  // 被解析成 std::vector of SomeType<false>,  
  5. // 在C++11中是合法的. (1>2) 這個表達式的結果爲false.  

 

5.12 顯式類型轉換操做符

    C++98 增長了explicit關鍵字來防止單參數的構造函數被用做隱式的類型轉換操做符.然而,卻沒有對真正的類型轉換操做符這樣作.例如,智能指針可能定義了operator bool(),讓它的行爲更像真原始指針.有了這個轉換操做符就能夠用if語句來測試:if (smart_ptr_variable),當這個指針不爲空時結果爲真,不然爲假.可是,這也會引發其餘一些非預期的轉換操做.因C++中的bool被定義爲一種算術類型,能夠隱式地轉換爲整數甚至是浮點數進行數學運算. 拿轉換出的布爾值進行非布爾計算的數學計算,每每不是程序員想要的.
  C++11中,explicit關鍵字也能夠用在類型轉換操做符上.和用在構造函數上同樣,防止這種轉換函數被於隱式類型轉換.固然了,在語言的上下文明確要求使用布爾變量(如if語句和循環語句中的條件,還有邏輯運算符的操做數)的地方,會被看成顯示轉換.因此可使用類型轉換操做符.

    5.13 模板的別名

  在進入這個主題前,先弄清楚"模板"和"類型"的區別.類型,是具體的數據類型,能夠直接用來定義變量. 模板,是類型的模板,根據這個模板能夠產生具體的類型;模板是不能直接定義變量的;當指定了全部的模板參數後,就產生了一個具體的類型,就能夠用來定義變量了.
  在C++03中,只能爲類型(包括徹底特化的模板,也是一種類型)定義別名, 而不能爲模板定義別名:  

[cpp]  view plain copy
 
  1. template <typename First, typename Second, int Third>  
  2. class SomeType;  
  3.    
  4. template <typename Second>  
  5. typedef SomeType<OtherType, Second, 5> TypedefName; // 在C++03中, 這是非法的.  

 

C++11增長爲模板定義別名的能力,用下面這樣的語法:

[cpp]  view plain copy
 
  1. template <typename First, typename Second, int Third>  
  2. class SomeType;  
  3.    
  4. template <typename Second>  
  5. using TypedefName = SomeType<OtherType, Second, 5>;  

 

  這種using語法也能夠用來定義類型的別名:  

[cpp]  view plain copy
 
  1. typedef void (*FunctionType)(double);       // 老式語法  
  2. using FunctionType = void (*)(double); // 新式語法<span style="rgb(255, 255, 255); font-family: verdana, 'ms song', Arial, Helvetica, sans-serif;"> </span>  

 

5.14 無限制的unions

  C++03中,對哪些類型的對象可以做爲聯合的成員是有限制的.例如,聯合不能包含定義了非平凡構造函數的對象.C++11廢除了其中的一些限制:
如今,聯合能夠包含定義了非平凡構造函數的對象;若是包含了,那麼聯合就必需要顯式定義一個構造函數.

[cpp]  view plain copy
 
  1. #include <new> // Required for placement 'new'.  
  2.    
  3. struct Point {  
  4.     Point() {}  
  5.     Point(int x, int y): x_(x), y_(y) {}  
  6.     int x_, y_;  
  7. };  
  8.    
  9. union U {  
  10.     int z;  
  11.     double w;  
  12.     Point p; // 非法的C++03; 合法的C++11.  
  13.     U() {new(&p) Point();} // 因爲Point的緣由, 必須定義構造函數.  
  14. };  

 

  由於是放寬了現有的規則,因此不會對已有的代碼形成影響. 

6 核心語言功能的改進

 這些特性讓C++語言能夠完成那些之前不可能的,極其繁瑣的或者須要一些不可移植的庫才能完成的事情.

6.1 可變參數模板  

  在 C++11 以前, 不管是類模板或是函數模板,都只能按其被聲明時所指定的樣子,接受一組數目固定的模板參數.C++11 加入新的表示法,容許任意個數,任意類型的模板參數,沒必要在定義時將參數的個數固定。

[cpp]  view plain copy
 
  1. template<typename... Values> class tuple;  

 

模板類 tuple 的對象,能接受不限個數的 typename 做爲它的模板形參:

[cpp]  view plain copy
 
  1. class tuple<int, std::vector<int>, std::map<std::string, std::vector<int>>> someInstanceName;  

 

實參的個數也能夠是0,因此 class tuple<> someInstanceName 這樣的定義也是能夠的。

若不但願產生實參個數爲 0 的變長參數模板,則能夠採用如下的定義:

[cpp]  view plain copy
 
  1. template<typename First, typename... Rest> class tuple;  

 

  變長參數模板也能運用到函數模板上。傳統 C 中的 printf 函數,雖然也能作到不定個數的形參來調用,但其並不是類型安全的。如下的例子中,C++11 除了能定義類型安全的變長參數函數外,還能讓相似 printf 的函數能天然地處理自定義類型的對象。 除了在模板參數中能使用...表示不定長模板參數外,函數參數也使用一樣的表示法表明不定長參數。

[cpp]  view plain copy
 
  1. template<typename... Params> void printf(const std::string &strFormat, Params... parameters);  

 

  其中,Params 與 parameters 分別表明模板與函數的變長參數集合,稱之爲參數包 (parameter pack).參數包必需要和運算符"..."搭配使用,避免語法上的歧義。

  變長參數模板中,沒法像在類或函數中那樣使用參數包.所以典型的作法是以遞歸的方法取出可用參數,請看如下的 C++11 printf 例子:

[cpp]  view plain copy
 
  1. void printf(const char *s)  
  2. {  
  3.   while (*s)  
  4.   {  
  5.     if (*s == '%' && *(++s) != '%')  
  6.       throw std::runtime_error("invalid format string: missing arguments");  
  7.     std::cout << *s++;  
  8.   }  
  9. }  
  10.    
  11. template<typename T, typename... Args>  
  12. void printf(const char* s, T value, Args... args)  
  13. {  
  14.   while (*s)  
  15.   {  
  16.     if (*s == '%' && *(++s) != '%')  
  17.     {  
  18.       std::cout << value;  
  19.       printf(*s ? ++s : s, args...); // 即使當 *s == 0 也會產生調用,以檢測更多的類型參數。  
  20.       return;  
  21.     }  
  22.     std::cout << *s++;  
  23.   }  
  24.   throw std::logic_error("extra arguments provided to printf");  
  25. }  

 

  printf 會不斷地遞歸調用自身:函數參數包 args... 在調用時, 會被模板類匹配分離爲 T value和 Args... args.直到 args... 變爲空參數,則會與簡單的 printf(const char *s) 造成匹配,退出遞歸。

  另外一個例子爲計算模板參數的個數,這裏使用類似的技巧展開模板參數包 Args...:

[cpp]  view plain copy
 
  1. template<>  
  2. struct count<> {  
  3.     static const int value = 0;  
  4. };  
  5.    
  6. template<typename T, typename... Args>  
  7. struct count<T, Args...> {   
  8.     static const int value = 1 + count<Args...>::value;  
  9. };  

 

  雖然沒有一個簡潔的機制可以對變長參數模板中的值進行迭代,但使用運算符"..."還能在代碼各處對參數包施以更復雜的展開操做。舉例來講,一個模板類的定義以下:

[cpp]  view plain copy
 
  1. template <typename... BaseClasses> class ClassName : public BaseClasses...  
  2. {  
  3. public:  
  4.    
  5.    ClassName (BaseClasses&&... baseClasses) : BaseClasses(baseClasses)... {}  
  6. }  

 

  BaseClasses... 會被展開成類型 ClassName 的基底類; ClassName 的構造函數須要全部基類的左值引用,而每個基類都是以傳入的參數作初始化 (BaseClasses(baseClasses)...)。

  在函數模板中,變長參數能夠和左值引用搭配,達成形參的完美轉發 (perfect forwarding):

[cpp]  view plain copy
 
  1. template<typename TypeToConstruct> struct SharedPtrAllocator  
  2. {  
  3.   template<typename... Args> std::shared_ptr<TypeToConstruct> ConstructWithSharedPtr(Args&&... params)  
  4.   {  
  5.     return tr1::shared_ptr<TypeToConstruct>(new TypeToConstruct(std::forward<Args>(params)...));  
  6.   }  
  7. }  

 

  參數包 parms 可展開爲 TypeToConstruct 構造函數的形參。 表達式std::forward<Args>(params) 可將形參的類別信息保留(利用右值引用),傳入構造函數。 而運算符"..."則能將前述的表達式套用到每個參數包中的參數。這種工廠函數(factory function)的手法, 使用 std::shared_ptr 管理配置對象的內存,避免了不當使用所產生的內存泄漏(memory leaks)。

  此外,變長參數的數量能夠藉如下的語法得知:

[cpp]  view plain copy
 
  1. template<typename ...Args> struct SomeStruct  
  2. {  
  3.   static const int size = sizeof...(Args);  
  4. }  

 

  SomeStruct<Type1, Type2>::size 是 2,而 SomeStruct<>::size 會是 0。 (sizeof...(Args) 的結果是編譯期常數。)

6.2 新的字符串字面值

  C++03提供兩種字符串字面值.第一種,包含在一對雙引號內,產生一個以空字符結尾的const char數組.第二種,由L""定義,產生以空字符結尾的const wchar_t類型的數組.wchar_t是一個大小和定義都未明肯定義的寬字符.字符串字面值既不支持UTF-8,UTF-16也不支持其它任何類型的unicode編碼.
  char類型的定義被修改了,明確表述爲:char的大小至少能存儲UTF-8的8位編碼,而且要足夠大到可以存儲編譯器實際使用的字符集的任何成員.這之前只在C++標準的後半部分中有定義,依靠C標準來保證char的大小至少爲8位.
  C++11支持3種UNICODE編碼: UTF-8, UTF-16, 和 UTF-32.除了前面提到的char類型定義的修改,C++11還增長了兩種字符類型:char16_t 和 char32_t.這兩種類型是分別用來存儲UTF-16和UTF-32的.
   下面的例子展現瞭如何建立各類編碼類型的字符串字面值:

u8"I'm a UTF-8 string."
u"This is a UTF-16 string."
U"This is a UTF-32 string."

  第一個字符串的類型是const char[], 第二個字符串的類型是const char16_t[], 第三個字符串的類型是const char32_t[]. 建立Unicode字符串時,常常會直接插入Unicode編碼值到字符串中.爲此,C++11提供以下語法:

u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \U00002018."

  '\u'後面是一個16進制數字,不須要加0x前綴.標識符\u表明一個16位的Unicode碼點.要輸入32位的碼點,使用\U加上32位的16進制數.只能輸入有效的Unicode碼點.例如,U+D800—U+DFFF以前的碼點是被禁止的,由於他們被保留用做UTF-16編碼中的代理對.

  有時候咱們須要手動避免轉義某些字符,尤爲是在使用xml文件,腳本語言或正則表達式等的字符串字面值時.C++提供了原始字符串:

R"(The String Data \ Stuff " )"
R"delimiter(The String Data \ Stuff " )delimiter"

  6.3 用戶自定義的字面值

  C++03提供幾種字面值.字符串"12.5"會被編譯器解析爲double類型的值12.5. 可是,帶有'f'後綴的字符串"12.5f"會建立一個float類型的值12.5. 後綴修飾符已經被C++標準固定下來了, 用戶代碼不能增長新的修飾符!
  C++11增長了讓用戶定義新的字面值修飾符的能力, 新的修飾符會基於被修飾的字符串來構造對象.
  字面值的轉換能夠分爲兩個階段:原始的和轉換後的(raw and cooked).原始的字面值是指某種特定類型的字符序列, 而加工過的字面值則表明另外一種類型.C++的字面值1234, 原始的字面值就是字符序列'1','2','3','4';而轉換後的字面值是整數1234. C++字面值0xA,轉換前的字面值是'0','x','A',轉換後就是整數10.字面值原始和轉換後的形式均可以被擴展.但字符串除外,它只有轉換後的形式能夠被擴展.這個例外是由於考慮到字符串有着會影響字符的特定意義和類型的前綴. 全部的用戶自定義字面值都是加後綴的, 想定義加前綴的字面值是不可能的.
  自定義字面值原始形式的處理定義以下:

OutputType operator "" _suffix(const char * literal_string); 
OutputType some_variable = 1234_suffix;

  第二個語句執行由自定義字面值定義的函數代碼.
  另外一種處理整數和浮點數原始字面值的機制是經過可變參數模板:

template<char...> OutputType operator "" _tuffix();
 
OutputType some_variable = 1234_tuffix;
OutputType another_variable = 2.17_tuffix;

  這個例子展現瞭如何以operator "" _tuffix<'1', '2', '3', '4'>()函數處理字面值.這種形式中,字符串沒有結尾的空字符!這麼作的主要目的是爲了使用C++11的constexpr關鍵字和編譯器能在編譯期就徹底轉換這些字面值.
  對於數字字面值,整數字面值轉換後的類型是unsigned long long,浮點數字面轉換後的類型是long double.沒有模板形式:

OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _suffix(long double);
 
OutputType some_variable = 1234_suffix; // Uses the 'unsigned long long' overload.
OutputType another_variable = 3.1416_suffix; // Uses the 'long double' overload.

  對於字符串字面值,下面的例子用了前面提到的新的字符串後綴.

複製代碼
OutputType operator "" _ssuffix(const char     * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const wchar_t  * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char32_t * string_values, size_t num_chars);
 
OutputType some_variable =   "1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable = u8"1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable =  L"1234"_ssuffix; // Uses the 'const wchar_t *'  overload.
OutputType some_variable =  u"1234"_ssuffix; // Uses the 'const char16_t *' overload.
OutputType some_variable =  U"1234"_ssuffix; // Uses the 'const char32_t *' overload.
複製代碼

 

6.4 多線程內存模型

  內存模型容許編譯器完成很重要的優化.即便像移動程序中的語句來合併循環這樣簡單的編譯器優化都可以影響對潛在共享變量讀,寫操做的順序!改變讀寫順序會致使競態條件的產生.沒有內存模型,編譯器通常不能將這種優化應用到多線程程序中的,或者只能用於某些特殊狀況.現代程序設計語言,好比Java,爲此實現了一個內存模型.內存模型指定了同步屏障(Synchronization Barriers),經過特殊的、定義好的同步操做(好比得到一個進入同步塊或某方法的鎖)來創建的.內存模型規定,共享變量值的改變只須要對那些經過了同步屏障的線程是可見的.此外,競態條件這個概念的完整定義覆蓋了帶有內存屏障細節的操做順序. 這些語義給了編譯器更高的自由度去進行優化: 編譯器只須要確保優化前和優化後同步屏障內的變量(可能被共享)的值是同樣的.
  大多數關於內存模型的研究都是圍繞着如下主題進行的:
  設計一個能讓編譯器有最大的優化自由度,同時還能對自由競爭提供足夠保障的內存模型;
  提供關於這種內存模型的正確的程序優化. 
  C++11標準支持多線程編程.這包含兩個部分:同一個程序中容許有多個線程同時存在和庫支持線程之間的交互;內存模型定義了何時多個線程可以訪問同一個內存地址,並指定了何時一個線程對內存的修改對另外一個線程是可見的!(參見:7.2 線程設施).

6.5 線程局部存儲

  在多線程環境中,線程一般都有一些本身所獨有的變量. 函數的局部變量也是這樣, 可是全局變量和靜態變量就不同了.
  新的線程局部存儲的生存期(原有的靜態,動態,自動變量除外)由thread_local關鍵字指定. 靜態對象的生存期也可能會被thread-local生存期替代.這麼作的目的是讓thread-local(線程局部)生存期的對象能夠像其餘靜態對象同樣由構造函數建立,由析構函數銷燬.

  6.6 明確默認和明確刪除的特殊成員函數 

  C++03中,若是類沒有定義構造函數,拷貝構造函數,賦值函數和析構函數的話,編譯器會爲類提供這些函數.程序員能夠本身定義這些函數來覆蓋編譯生成的默認版本.C++還定義了幾個能夠做用在全部類上的操做符(好比,賦值操做符=,new操做符等),程序員也能夠覆蓋它們.
  然而, 對這些默認函數的建立只有不多的控制.例如, 要生成一個不可拷貝的類必需要聲明私有的拷貝構造函數和私有的賦值操做符而且不定義它們的實現.試圖調用這些函數就會違反"一個定義原則"(ODR,一個函數能夠被調用,那麼這個函數必須且只能有一個函數體定義).儘管診斷信息不是必須的,可是這類違規行爲可能會致使連接錯誤.
  就構造函數而言, 只要一個類定義了任意一個構造函數,編譯器就不會自動爲它生成構造函數了.這在不少狀況下是頗有用的,但有些狀況下用戶定義了這些函數,編譯器還生成這些函數也是頗有用的.
  C++11容許顯式指明要不要使用這些特殊的成員函數.例如,下面的聲明顯式指出要使用默認構造函數:

struct SomeType {
    SomeType() = default; //The default constructor is explicitly stated.
    SomeType(OtherType value);
};

  另外一方面,一些特性能夠被顯式地禁用.例如,下面的類是不可拷貝的:

struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable & operator=(const NonCopyable&) = delete;
};

  指示符 = delete 能夠用來阻止任何函數被調用,能夠用來禁止調用帶特定參數的成員函數.例如:

struct NoInt {
    void f(double i);
    void f(int) = delete;    // 不能調用這個函數
};

  編譯器會拒絕試圖對帶int參數的函數f()的調用, 而不是默默地轉換爲對帶有double參數的f()的調用.這能夠泛化到禁止除了帶double參數外其餘任何參數類型的f()的調用.例如:

struct OnlyDouble {
    void f(double d);
    template<class T> void f(T) = delete;    //不能調用這個函數
};

 

6.7 long long int類型

  C++03中,最大的整數類型是long int.它保證使用的位數至少與int同樣. 這致使long int在一些實現是64位的, 而在另外一些實現上倒是32位的.C++11增長了一個新的整數類型long long int來彌補這個缺陷.它保證至少與long int同樣大,而且很多於64位.這個類型早在C99就引入到了標準C中, 並且大多數C++編譯器都以擴展的形式支持這種類型了.

6.8 靜態斷言

  C++03提供兩種方法來測試斷言:宏assert和#error預處理指令.然而,這不適合用在模板中:宏在運行期間測試斷言,而預處理指令在編譯預處理階段測試斷言,這些都發生在模板實例化以前;也不適合用於依賴於模板參數的屬性.
  C++11引入了一個新的關鍵字static_assert在編譯期測試斷言.聲明呈現下面這樣的形式:

複製代碼
static_assert (constant-expression, error-message);
    //下面幾個例子展現怎樣使用static_assert:
static_assert((GREEKPI > 3.14) && (GREEKPI < 3.15), "GREEKPI is inaccurate!");
template<class T>
struct Check  {
    static_assert(sizeof(int) <= sizeof(T), "T is not big enough!");
};
template<class Integral>
Integral foo(Integral x, Integral y) {
    static_assert(std::is_integral<Integral>::value, "foo() parameter must be an integral type.");
}
複製代碼

  當常量表達的結果爲false時,編譯器就會產生一個錯誤消息.第一個例子相似於預處理指令#error,可是預處理指令只支持整數類型.相比之下,第二例子中的斷言在模板類Check每一次被實例化的時候都被檢查一次.
  除了模板以外,靜態斷言也是頗有用的.例如:一個算法的某個實現依賴於long long類型必須大於int,這類型事情標準並無作出保證.這種假設在大多數系統和編譯器上是有效的,但毫不是所有!

6.9 容許sizeof運算符做用在類型的數據成員上,無須明確的對象

  C++03中,sizeof能夠做用在類和對象上.但卻不能像下面這樣作:

struct SomeType { OtherType member; };
 
sizeof(SomeType::member); // C++03 不行. C++11 能夠.
    //這會返回OtherType的大小.C++03不容許這樣作,會報一個編譯錯誤.C++11容許這樣作.

 

6.10 控制和查詢對象的對齊方式

  C++11能夠用alignof和alingas來查詢和控制變量的對齊方式.

  alignof是一個操做符,他以一個類型爲參數,而且返回這個類型的實例必須分配的字節邊界值,這個值必定是2的整數次冪.若是參數是引用類型,那麼返回的是被引用的類型的對齊信息.對於數組,返回的是元素類型的對齊信息.
 laignas指示符變量的內存控制方式.這個指示符的參數是一個常量或一個類型, alignas(T)是alignas(alignof(T))的簡寫形式.例如:下面的例子聲明一個char數組,它的對齊方式與float型數據同樣.
  alignas(float) unsigned char c[sizeof(float)]

  6.11 容許實現垃圾回收

  以前版本的C++標準經過set_new_handler提供了程序員驅動的垃圾回收機制,但卻沒有爲自動化垃圾回收機制給出對象可到達性的定義. C++11定義了指針徹底地從其餘地方得到值的條件.編譯器實現能夠指定在嚴格的指針安全下進行操做,在這種狀況下不按這個規則得到值的指針就會變成無效的.

  6.12 屬性
  C++11爲編譯器和其餘工具提供了標準的語言擴展語法.這些擴展從來都是用#pragma指令或生產商指定的關鍵字(如GNU的__attributes__和微軟的 __declspec).C++11有了新的語法, 以雙重方括號的形式爲屬性指定額外的信息.屬性能夠被用於各類代碼元素:

int [[attr1]] i [[attr2, attr3]];
 
[[attr4(arg1, arg2)]] if (cond)
{
    [[vendor::attr5]] return i;
}

   在上面的例子中,屬性attr1做用在變量i的類型int上,而attr2和attr3則做用於變量i自己.attr4做用於if語句,vendor::attr5做用於return語句.通常地(但有一些例外),爲一個命名實體指定的屬性放在實體名字以後,其餘部分以前.多個屬性能夠放在一個雙重方括號對中,像上面的例子那樣. 屬性可能會有附加的參數,屬性也可能被放在生產商指定的屬性命名空間中.
  建議屬性不要有任何語言上的意義,也不要改變程序的觀感. 屬性能夠提供一些頗有用的信息,例如幫助編譯器生成更好的診斷信息或優化生成的代碼.
  C++11自己提供兩種標準的屬性:noreturn屬性指出函數沒有返回值, carries_dependency屬性經過指出函數的參數或返回值有依賴關係來幫助優化多線程代碼.

7. C++標準程序庫的變化

  C++11標準庫引入了不少新特性.不少是在舊標準下實現的,可是有一些卻依賴於C++11的核心特性.新標準庫的大部分是在2005年公佈的C++標準委員會標準庫技術報告(tr1)中定義的.各類徹底或部分的TR1實如今現行的標準中能夠經過命名空間std::tr1來引用了.對於C++11,這些實現被移到了命名空間std中.然而,由於TR1的特性被引入到了C++11的標準庫中,因此須要更新它們以適合那些在最初的TR1中不可用的C++11特性.
  C++11的標準化已經完成,標準委員會打算建立第二版標準庫技術報告(TR2).那些被提議但卻沒來及加入C++11的庫,將會被放入TR2或之後的技術報告中.

7.1 標準庫組件的升級

  C++11提供了不少現存標準庫組件能從中獲益的新特性.例如,大多數標準容器均可以從基於移動構造右值引用中獲益,無論是快速移動重型容器仍是把容器的內容移動到新的內存位置.標準庫組件已經用適當的C++11新特性升級過了.包括但不限於如下特性:

  • 右值引用及其關聯的移動支持
  • 支持utf-16和utf-32編碼的unicode字符類型
  • 可變模板(加上右值引用能夠實現完美轉發)
  • 編譯期常量表達式
  • decltype
  • 顯式類型轉換操做符
  • default/deleted成員函數

  7.2 線程支持

  C++11雖然從語言上提供了支持線程的內存模型,但主要的支持仍是來自標準庫.
  新的標準庫提供了一個線程類(std::thread)來運行一個新線程,它帶有一個函數對象參數和一系列可選的傳遞給函數對象的參數.經過std::thread::join()支持的線程鏈接操做可讓一個線程直到另外一個線程執行完畢才中止.std:thread::native_handle()成員函數提供了對底層本地線程對象的可能且合理的平臺相關的操做.
  爲支持線程同步,標準庫增長了互斥體(std::mutex, std::recursive_mutex等)和條件變量(std::condition_variable 和std::condition_variable_any).這些都是經過RAII鎖和加鎖算法就能夠簡單使用的.
  有時爲了高性能或底層工做,要求線程間的通訊沒有開銷巨大的互斥鎖.原子操做能夠達到這個目的,這能夠隨意地爲一個操做指定最小的內存可見度.顯式的內存屏障也能夠用於這個目的.
C++11線程庫還包含了futures和promises,用於在線程間傳遞異步結果.而且提供了std::packaged_task來封裝能夠產生這種異步結果的函數調用.
  更高級的線程支持,如線程池,已經決定留待在將來的 Technical Report 加入此類支持。更高級的線程支持不會是 C++11 的一部份,可是其最終實現將創建在目前已有的線程支持之上。std::async 提供了一個簡便方法來運行線程,並將線程綁定在 std::future上。用戶能夠選擇一個工做是要在多個線程上異步的運行,仍是在一個線程上運行並等待其所須要的數據。默認的狀況,實現能夠根據底層硬件選擇前面兩個選項的其中之一。另外在較簡單的使用場景下,實現也能夠利用線程池提供支持。

7.3 元組類型

  元組(tuple)由預先肯定數量的多種對象組成.元組能夠看做是struct數據成員的泛化.TR1 tuple類型的C++11版本獲益於像可變參數模板這樣的C++11語言特性.TR1版本的元組須要一個由實現定義的包含的類型的最大數目,並且須要大量的宏技巧來實現.相比之下,C++11版本的不須要顯式的實現定義的最大類型數目.儘管編譯器有一個內部的模板實例化的最大遞歸深度,但C++11版的元組不會把它暴露給用戶.
  用可變參數模板,元組類的定義看上去像下面這樣:

複製代碼
template <class ...Types> class tuple;
//下面是定義和使用元組的一個例子:
typedef std::tuple <int, double, long &, const char *> test_tuple;
long lengthy = 12;
test_tuple proof (18, 6.5, lengthy, "Ciao!");
lengthy = std::get<0>(proof);  // 把'lengthy' 賦值爲18.

std::get<3>(proof) = " Beautiful!";  // 修改元組的第四個元素
複製代碼

  能夠在定義一個元組時不定義他的內容,不過只有當它的每一個元素類型都有默認構造函數時才能夠這樣作.並且,能夠將一個元組賦值給另外一個元組:若是兩個元組的類型相同,且每種元素類型都有拷貝構造函數;不然,須要右邊的元組的元素能夠隱式轉換成左邊元組的對應元素類型或者左邊元組的元素類型有合適的構造函數.

typedef std::tuple <int , double, string       > tuple_1 t1;
typedef std::tuple <char, short , const char * > tuple_2 t2 ('X', 2, "Hola!");
t1 = t2; // Ok, 前兩個元素都是能夠轉換的.
            // 第三個能夠用'const char *'來構造一個string對象.

  能夠對元組類型對象的進行比較運算(當它們擁有一樣數量的元素)。此外,C++11 提供兩個表達式用來檢索元組類型的屬性(僅在編譯期作此檢查)。

std::tuple_size<T>::value //返回元組 T 內的元素個數,
std::tuple_element<I, T>::type //返回元組 T 內的第 I 個元素的類型

 

    7.4 散列表

   在過去,不斷有要求想將散列表(無序關係式容器)引進標準庫。只由於時間上的限制,散列表纔沒有被標準庫所採納。雖然,散列表在最糟狀況下(若是出現許多衝突 (collision) 的話)在性能上比不過平衡樹。但實際運用中,散列表的表現則較好。
由於標準委員會還看不到有任何機會能將開放尋址法標準化,因此目前衝突僅能經過線性鏈(linear chaining) 的方式來處理。爲避免與第三方庫發展的散列表發生名稱上的衝突,前綴將採用 unordered 而非 hash。
  標準庫將引進四種散列表,其中差異在於如下兩個特性: 是否接受具相同鍵值的項(Equivalent keys),以及是否會將鍵值映射到相對應的數據(Associated values).新的標準庫增長了如下散列表類型:

散列表類型 有無關係值 接受相同鍵值
std::unordered_set
std::unordered_multiset
std::unordered_map
std::unordered_multimap

  這些類徹底具有容器類需的條件,同時也提供訪問其中元素的成員函數: insert, erase, begin, end。
  散列表不須要對現有核心語言作擴展(雖然散列表的實現會利用到 C++11 新的語言特性),只會對頭文件 <functional> 作些許擴展,並引入 <unordered_set> 和 <unordered_map> 兩個頭文件。對於其它現有的類型不會有任何修改。同時,散列表也不會依賴標準庫的其它擴展功能。

7.5 正則表達式

  新的標準庫定義了一個新的頭文件<regex>,由一些新的類組成:

  • 正則表達式由模板類std::regex的實例來表示;
  • 模式匹配由的結果模板類std::match_results的實例來表示;

  函數 regex_search 是用來搜索模式的; 若要搜索並替換,則要使用函數 regex_replace,該函數會返回一個新的字符串。算法regex_search 和 regex_replace 接受一個正則表達式(模式)和一個字符串,並將該模式匹配的結果狀況存儲在 struct match_results對象中。
下面的例子展現了 match_results 的用法:

複製代碼
const char *reg_esp = "[ ,.\\t\\n;:]";  // 列出分隔符.
// 這也能夠經過字符串字面值來完成:
// const char *reg_esp = R"([ ,.\t\n;:])";
std::regex rgx(reg_esp); // 'regex' 是模板類'basic_regex'以'char'爲類型參數特化的類.
std::cmatch match; // 'cmatch'是模板類'match_results'以'const char *'特化的類.
const char *target = "Unseen University - Ankh-Morpork";
 
// 找出'target'中全部以'reg_esp'中的字符分隔的單詞.
if (std::regex_search(target, match, rgx)) {
    // 若是找到了指定的單詞 
    const size_t n = match.size();
    for (size_t a = 0; a < n; a++) {
        std::string str (match[a].first, match[a].second);
        std::cout << str << "\n";
    }
}
複製代碼

  注意雙反斜槓的使用,由於 C++ 將反斜槓做爲轉義字符使用。但 C++11的原始字符串(raw string)能夠用來避免這一問題。庫 <regex> 不須要改動到現有的頭文件,同時也不須要擴展示有的語言特性。

7.6 通用智能指針

  這些指針是由 TR1 智能指針演變而來。注意! 智能指針是類而非通常指針。shared_ptr 是引用計數型(reference-counted) 指針類,其行爲與通常 C++ 指針極爲類似。在 TR1 的實現中,缺乏了一些通常指針所擁有的特性,像是別名或是指針運算。C++11增長了這些特性。如下是一個使用 shared_ptr 的例子:

複製代碼
int main( )
{
    std::shared_ptr<double> p_first(new double) ;
    {
        std::shared_ptr<double> p_copy = p_first ;
        *p_copy = 21.2;
    }  // 此時 'p_copy' 會被銷燬,但動態分配的 double 不會被銷燬。
 
    return 0;  // 此時'p_first'會被銷燬,但動態分配的 double也會被銷燬(由於再也不有指針指向它)。
}
複製代碼

  auto_ptr 將會被 C++ 標準所廢棄,取而代之的是unique_ptr。 unique_ptr 提供 auto_ptr 大部份特性,但不包括 auto_ptr 的不安全性和隱性的左值轉移。不像 auto_ptr,unique_ptr 能夠存放在 C++11 提出的那些須要移動語義的容器之中。

7.7 可擴展的隨機數生成器

  C 標準庫容許使用rand函數來生成僞隨機數。不過其算法則取決於各程序庫開發者。 C++ 直接從 C 繼承了這部份,可是 C++11 將會提供產生僞亂數的新方法。C++11 的隨機數功能分爲兩部分: 第一,一個隨機數生成引擎,其中包含該生成引擎的狀態,用來產生隨機數。第二,一個分佈,這能夠用來決定產生隨機數的範圍,也能夠決定以何種分佈方式產生隨機數。隨機數生成對象便是由隨機數生成引擎和分佈所構成。

  不一樣於 C 標準庫的 rand; 針對產生隨機數的機制,C++11 將會提供三種算法,每一種算法都有其強項和弱項:

樣板類 整數/浮點數 品質 速度 狀態數*
linear_congruential 整數 中等 1
subtract_with_carry 二者皆可 中等 25
mersenne_twister 整數 624

  C++11 將會提供一些標準分佈: uniform_int_distribution (離散型均勻分佈),bernoulli_distribution (伯努利分佈),geometric_distribution (幾何分佈), poisson_distribution (卜瓦松分佈),binomial_distribution (二項分佈),uniform_real_distribution (離散型均勻分佈), exponential_distribution (指數分佈),normal_distribution (正態分佈) 和 gamma_distribution (伽瑪分佈)。下面的例子展現了一個隨機數生成對象如何由生成引擎和分佈構成的:

std::uniform_int_distribution<int> distribution(0, 99); // 創建分佈,以離散均勻分佈方式在0到99之間產生隨機數
std::mt19937 engine; // 創建隨機數生成引擎
auto generator = std::bind(distribution, engine); // 利用 bind 隨機數生成引擎和分佈組合成一個隨機數生成器
int random = generator();  // 產生隨機數

  7.8 封裝引用

  咱們能夠經過實例化模板類 reference_wrapper 獲得一個封裝引用 (wrapper reference)。封裝引用相似於通常的引用。對於任意對象,咱們能夠經過模板類 ref 獲得一個封裝引用 (至於 constant reference 則可經過 cref 獲得)。當模板函數須要形參的引用而非其拷貝時封裝引用就能派上用場了:

複製代碼
// 此函數將獲得r的引用,並將r的值加1.
void f (int &r)  { r++; }
 
// 模板函數
template<class F, class P> void g (F f, P t)  { f(t); }
 
int main()
{
    int i = 0 ;
    g (f, i) ;  // 實例化 'g<void (int &r), int>' 
                // 'i' 不會被修改
    std::cout << i << std::endl;  // 輸出 0
 
    g (f, std::ref(i));  // 實例化 'g<void(int &r),reference_wrapper<int>>'
                         // 'i' 會被修改
    std::cout << i << std::endl;  // 輸出 1
}
複製代碼

  這項功能將加入頭文件 <utility> 之中,而非經過擴展語言來獲得.

7.9 多態的函數對象包裝器

  函數對象的多態包裝器(又稱多態函數對象包裝器)在語義和語法上和函數指針類似,但不像函數指針那麼狹隘。只要能被調用,且其參數能與包裝器兼容的都能以稱之爲多態函數對象包裝器(函數指針,成員函數指針或仿函數)。

  經過如下例子,咱們能夠了解多態函數對象包裝器的特性:

複製代碼
std::function<int (int, int)> func;  // 利用模板類 'function'
                                     // 創建包裝器
std::plus<int> add;  // 'plus' 被聲明 'template<class T> T plus( T, T ) ;'
                     //  所以 'add' 的類型是 'int add( int x, int y )'
func = &add;  // 可行。'add' 的型參和回返值類型與 'func' 相符
 
int a = func (1, 2);  // 注意: 若包裝器 'func' 沒有引用到任何函數
                      // 會拋出 'std::bad_function_call' 異常
 
std::function<bool (short, short)> func2 ;
if(!func2) { // 由於還給'func2'賦值,此表達式爲真
 
    bool adjacent(long x, long y);
    func2 = &adjacent ;  // 可行。'adjacent' 的型參和回返值類型可經過類型轉換與'func2'相符
 
    struct Test {
        bool operator()(short x, short y);
    };
    Test car;
    func = std::ref(car);  // 模板類 'std::ref' 返回一個struct 'car' 成員函數 'operator()' 的包裝
}
func = func2;  // 可行。'func2'的型參和回返值類型可經過類型轉換而與'func' 相符
複製代碼

  模板類 function 將定義在頭文件 <functional>,而不須改動語言自己。

7.10 用於元編程的類型特徵

  編寫一個建立或修改其它程序(也能夠是程序自己)的程序稱爲元編程.這種行爲能夠發生在編譯期,也能夠發生在運行期.C++標準委員會決定引入一個庫,容許在編譯期利用模板進行元編程.
如下是一個元編程的例子,基於當前的C++03標準: 模板的遞歸實例化來計算整數的冪

複製代碼
template<int B, int N>
struct Pow {
    // 遞歸調用和從新組合.
    enum{ value = B*Pow<B, N-1>::value };
};
 
template< int B >
struct Pow<B, 0> {
    // ''N == 0'' 是終止條件.
    enum{ value = 1 };
};
int quartic_of_three = Pow<3, 4>::value;
複製代碼

  不少算法均可以操做不一樣類型的數據;C++的模板支持泛型編程,而且使代碼更加緊湊和有用.然而算法一般都須要知道它正使用的數據的類型.這些信息能夠在編譯期利用類型特徵(type trait)獲取到. 類型特徵(type traits)能夠標識出一個對象的類別和類或結構體的所有特徵.type traits定義在一個新頭文件<type_traits>中.
在下面的例子中,模板函數elaborate根據給定的數據類型實例化了一個特定的算法(algorithms.do_it)

複製代碼
// 算法一
template< bool B > struct Algorithm {
    template<class T1, class T2> static int do_it (T1 &, T2 &)  { /*...*/ }
};
 
// 算法二
template<> struct Algorithm<true> {
    template<class T1, class T2> static int do_it (T1, T2)  { /*...*/ }
};
 
// 根據給定的類型,會自動實例化正確的算法.
template<class T1, class T2>
int elaborate (T1 A, T2 B)
{
    // 只有當T1是整數,T2是浮點數是實例化算法二
    // 其餘狀況實例化算法一
    return Algorithm<std::is_integral<T1>::value && std::is_floating_point<T2>::value>::do_it( A, B ) ;
}
複製代碼

  經過定義在<type_transform>頭文件中的類型特徵,能夠建立類型轉換操做(static_cast和const_cast對模板來講是不夠的).這種編程技術能產生優雅簡潔的代碼,可是除錯倒是這種技術的弱點:編譯期的錯誤信息難以理解,運行期錯誤則更難.

7.11 用於計算函數對象返回類型的統一方法

要在編譯期肯定一個模板函數的返回值類型不是那麼容易的,特別是當返回類型依賴於函數參數的時候.例如:

複製代碼
struct Clear {
    int    operator()(int) const;    // 參數類型
    double operator()(double) const; // 與返回類型相同
};
 
template <class Obj>
class Calculus {
public:
    template<class Arg> Arg operator()(Arg& a) const {
        return member(a);
    }
private:
    Obj member;
};
//以Clear來實例化Calculus模板類(Calculus<Clear>), Calculus類的全部對象都有與Clear類相同的返回類型.可是下面給出的Confused類:
struct Confused {
    double operator()(int) const;     // 參數類型
    int    operator()(double) const;  // 不一樣於返回類型
};
//試圖實例化Calculus<Confused>會致使Calculus與Confused的返回類型不一樣.編譯會產生一條從int轉換到double的警告和一條從double轉換到int的警告信息.
//在TR1引入,C++11也接受了模板類std::result_of,它容許咱們在全部的聲明中肯定和使用函數對象的返回類型.下面的類CalculusVer2用std::result_of對象來得到函數對象的返回類型:
template< class Obj >
class CalculusVer2 {
public:
    template<class Arg>
    typename std::result_of<Obj(Arg)>::type operator()(Arg& a) const {
        return member(a);
    }
private:
    Obj member;
};
//這種實例化CalculusVer2<Confused>函數對象的方法沒有類型轉換,沒有警告也沒錯誤.
複製代碼

 

  模板 std::result_of 在 TR1 和 C++11 的實現中有一點不一樣。TR1 的版本容許實如今特殊狀況下,可能沒法決定一個函數調用其回返值的類別。然而,由於 C++11支持了decltype,實現被要求在全部狀況下,皆能計算出回返值類型。

 

7.12 本來計劃加入但沒有加入C++11的特性  

  預計由 Technical Report 提供支持的:

  • 模塊
  • 十進制類別
  • 數學專用函數

  延後討論的:

  • Concepts (概念 (C++))
  • 更完整或必備的垃圾回收支持
  • Reflection 反射
  • Macro Scopes 宏做用域

7.13 被移除或被廢棄的特性  

  • 順序點 (sequence point),這個術語正被更爲易懂的描述所取代。一個運算能夠發生 (is sequenced before) 在另外一個運算以前; 又或者兩個運算彼此之間沒有順序關係 (are unsequenced)。
  • export,用法已被去掉,但關鍵字仍是被保留了,給未來可能的特性使用
  • exception specifications
  • std::auto_ptr 被 std::unique_ptr 取代。
  • 函數對象的基類(std::unary_function, std::binary_function)、函數指針適配器、類型成員指針適配器以及綁定器 (binder)。

  

  結尾: 衷心但願看到的朋友能給點意見或建議

C++QQ羣:231550332,歡迎對C++感興趣的朋友一塊兒來交流學習.
相關文章
相關標籤/搜索