轉:c++ 11 新特性

聲 明:本文源自 Danny Kalev 在 2011 年 6 月 21 日發表的《The Biggest Changes in C++11(and Why You Should Care)》一文,幾乎全部內容都搬了過來,但不是全文照譯,有困惑之處,請參詳原文(http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/ )。
注:做者 Danny Kalev 曾是 C++ 標準委員會成員。程序員

Lambda 表達式

Lambda 表達式的形式是這樣的:正則表達式

 

  1. [capture](parameters)->return-type {body}    

來看個計數某個字符序列中有幾個大寫字母的例子:算法

 

  1. int main()    
  2. {    
  3.    char s[]="Hello World!";    
  4.    int Uppercase = 0; //modified by the lambda    
  5.    for_each(s, s+sizeof(s), [&Uppercase] (char c) {    
  6.     if (isupper(c))    
  7.      Uppercase++;    
  8.     });    
  9.  cout<< Uppercase<<" uppercase letters in: "<< s<<endl;    
  10. }    

其中 [&Uppercase] 中的 & 的意義是 lambda 函數體要獲取一個 Uppercase 引用,以便可以改變它的值,若是沒有 &,那就 Uppercase 將以傳值的形式傳遞過去。數組

自動類型推導和 decltype

在 C++03 中,聲明對象的同時必須指明其類型,其實大多數狀況下,聲明對象的同時也會包括一個初始值,C++11 在這種狀況下就可以讓你聲明對象時再也不指定類型了:promise

 

  1. auto x=0; //0 是 int 類型,因此 x 也是 int 類型    
  2. auto c='a'; //char    
  3. auto d=0.5; //double    
  4. auto national_debt=14400000000000LL;//long long    

這個特性在對象的類型很大很長的時候頗有用,如:安全

  1. void func(const vector<int> &vi)    
  2. {    
  3.   vector<int>::const_iterator ci=vi.begin();    
  4. }    

那個迭代器能夠聲明爲:併發

  1. auto ci=vi.begin();   

C++11 也提供了從對象或表達式中「俘獲」類型的機制,新的操做符 decltype 能夠從一個表達式中「俘獲」其結果的類型並「返回」:socket

  1. const vector<int> vi;    
  2. typedef decltype (vi.begin()) CIT;    
  3. CIT another_const_iterator;    

 

統一的初始化語法

C++ 最少有 4 種不一樣的初始化形式,如括號內初始化,見:async

  1. std::string s("hello");    
  2. int m=int(); //default initialization   

還有等號形式的:ide

  1. std::string s="hello";    
  2. int x=5;    

對於 POD 集合,又能夠用大括號:

  1. int arr[4]={0,1,2,3};    
  2. struct tm today={0};    

最後還有構造函數的成員初始化:

  1. struct S {    
  2.  int x;    
  3.  S(): x(0) {} };    

這麼多初始化形式,不只菜鳥會搞得很頭大,高手也吃不消。更慘的是 C++03 中竟然不能初始化 POD 數組的類成員,也不能在使用 new[] 的時候初始 POD 數組,操蛋啊!C++11 就用大括號一統天下了:

  1. class C    
  2. {    
  3. int a;    
  4. int b;    
  5. public:    
  6.  C(int i, int j);    
  7. };    
  8. C c {0,0}; //C++11 only. 至關於 C c(0,0);    
  9. int* a = new int[3] { 1, 2, 0 }; /C++11 only    
  10. class X {    
  11.   int a[4];    
  12. public:    
  13.   X() : a{1,2,3,4} {} //C++11, 初始化數組成員    
  14. };    

還有一大好事就是對於容器來講,終於能夠擺脫 push_back() 調用了,C++11中能夠直觀地初始化容器了:

  1. // C++11 container initializer    
  2. vector vs<string>={ "first", "second", "third"};    
  3. map singers =    
  4.   { {"Lady Gaga", "+1 (212) 555-7890"},    
  5.     {"Beyonce Knowles", "+1 (212) 555-0987"}};   

而類中的數據成員初始化也獲得了支持:

  1. class C    
  2. {    
  3.  int a=7; //C++11 only    
  4. public:    
  5.  C();    
  6. };    

 

deleted 函數和 defaulted 函數

像如下形式的函數:

  1. struct A    
  2. {    
  3.  A()=default; //C++11    
  4.  virtual ~A()=default; //C++11    
  5. };    

叫作 defaulted 函數,=default; 指示編譯器生成該函數的默認實現。這有兩個好處:一是讓程序員輕鬆了,少敲鍵盤,二是有更好的性能。
與 defaulted 函數相對的就是 deleted 函數:

  1. int func()=delete;    

這貨有一大用途就是實現 noncopyabe 防止對象拷貝,要想禁止拷貝,用 =deleted 聲明一下兩個關鍵的成員函數就能夠了:

  1. struct NoCopy    
  2. {    
  3.     NoCopy & operator =( const NoCopy & ) = delete;    
  4.     NoCopy ( const NoCopy & ) = delete;    
  5. };    
  6. NoCopy a;    
  7. NoCopy b(a); //編譯錯誤,拷貝構造函數是 deleted 函數    

 

nullptr

nullptr 是一個新的 C++ 關鍵字,它是空指針常量,它是用來替代高風險的 NULL 宏和 0 字面量的。nullptr 是強類型的:

  1. void f(int); //#1    
  2. void f(char *);//#2    
  3. //C++03    
  4. f(0); //調用的是哪一個 f?    
  5. //C++11    
  6. f(nullptr) //毫無疑問,調用的是 #2    

全部跟指針有關的地方均可以用 nullptr,包括函數指針和成員指針:

  1. const char *pc=str.c_str(); //data pointers    
  2. if (pc!=nullptr)    
  3.   cout<<pc<<endl;    
  4. int (A::*pmf)()=nullptr; //指向成員函數的指針    
  5. void (*pmf)()=nullptr; //指向函數的指針    

 

委託構造函數

C++11 中構造函數能夠調用同一個類的另外一個構造函數:

  1. class M //C++11 delegating constructors    
  2. {    
  3.  int x, y;    
  4.  char *p;    
  5. public:    
  6.  M(int v) : x(v), y(0),  p(new char [MAX])  {} //#1 target    
  7.  M(): M(0) {cout<<"delegating ctor"<<end;} //#2 delegating    

#2 就是所謂的委託構造函數,調用了真正的構造函數 #1。

右值引用

在 C++03 中的引用類型是隻綁定左值的,C++11 引用一個新的引用類型叫右值引用類型,它是綁定到右值的,如臨時對象或字面量。
增長右值引用的主要緣由是爲了實現 move 語義。與傳統的拷貝不一樣,move 的意思是目標對象「竊取」原對象的資源,並將源置於「空」狀態。當拷貝一個對象時,其實代價昂貴且無必要,move 操做就能夠替代它。如在 string 交換的時候,使用 move 意義就有巨大的性能提高,如原方案是這樣的:

  1. void naiveswap(string &a, string & b)    
  2. {    
  3.  string temp = a;    
  4.  a=b;    
  5.  b=temp;    
  6. }    


這種方案很傻很天真,很慢,由於須要申請內存,而後拷貝字符,而 move 就只須要交換兩個數據成員,無須申請、釋放內存和拷貝字符數組:

  1. void moveswapstr(string& empty, string & filled)    
  2. {    
  3. //pseudo code, but you get the idea    
  4.  size_t sz=empty.size();    
  5.  const char *p= empty.data();    
  6. //move filled's resources to empty    
  7.  empty.setsize(filled.size());    
  8.  empty.setdata(filled.data());    
  9. //filled becomes empty    
  10.  filled.setsize(sz);    
  11.  filled.setdata(p);    
  12. }    



要實現支持 move 的類,須要聲明 move 構造函數和 move 賦值操做符,以下:

  1. class Movable    
  2. {    
  3. Movable (Movable&&); //move constructor    
  4. Movable&& operator=(Movable&&); //move assignment operator    
  5. };    


C++11 的標準庫普遍使用 move 語義,不少算法和容器都已經使用 move 語義優化過了。

C++11 的標準庫

除 TR1 包含的新容器(unordered_set, unordered_map, unordered_multiset, 和unordered_multimap),還有一些新的庫,如正則表達式,tuple,函數對象封裝器等。下面介紹一些 C++11 的標準庫新特性:

線程庫

從程序員的角度來看,C++11 最重要的特性就是併發了。C++11 提供了 thread 類,也提供了 promise 和 future 用以併發環境中的同步,用 async() 函數模板執行併發任務,和 thread_local 存儲聲明爲特定線程獨佔的數據,這裏(http://www.devx.com/SpecialReports/Article/38883)有一個簡單 的 C++11 線程庫教程(英文)。

新的智能指針類

C++98 定義的惟一的智能指針類 auto_ptr 已經被棄用,C++11 引入了新的智能針對類 shared_ptr 和 unique_ptr。它們都是標準庫的其它組件兼容,能夠安全地把智能指針存入標準容器,也能夠安全地用標準算法「倒騰」它們。

新的算法

主要是 all_of()、any_of() 和 none_of(),下面是例子:

  1. #include <algorithm>    
  2. //C++11 code    
  3. //are all of the elements positive?    
  4. all_of(first, first+n, ispositive()); //false    
  5. //is there at least one positive element?    
  6. any_of(first, first+n, ispositive());//true    
  7. // are none of the elements positive?    
  8. none_of(first, first+n, ispositive()); //false    

 

還有一個新的 copy_n:

  1. #include <algorithm>    
  2. int source[5]={0,12,34,50,80};    
  3. int target[5];    
  4. //從 source 拷貝 5 個元素到 target    
  5. copy_n(source,5,target);    

iota() 算法能夠用來建立遞增序列,它先把初值賦值給 *first,而後用前置 ++ 操做符增加初值並賦值到給下一個迭代器指向的元素,以下:

  1. #include <numeric>    
  2. int a[5]={0};    
  3. char c[3]={0};    
  4. iota(a, a+5, 10); //changes a to {10,11,12,13,14}    
  5. iota(c, c+3, 'a'); //{'a','b','c'}    

 

是的,C++11 仍然缺乏一些頗有用的庫如 XML API,socket,GUI、反射——以及自動垃圾收集。然而現有特性已經讓 C++ 更安全、高效(是的,效率更高了,能夠參見 Google 的 基準測試結果http://www.itproportal.com/2011/06/07/googles-rates-c-most- complex-highest-performing-language/)以及更加易於學習和使用。

若是以爲 C++ 變化太大了,沒必要驚恐,花點時間來學習就行了。可能在你融會貫通新特性之後,你會贊成 Stroustrup 的觀點:C++11 是一門新的語言——一個更好的 C++。

相關文章
相關標籤/搜索