掀起C++ 11的神秘面紗
原創(chuàng)【51CTO 6月21日外電頭條】C++之父Bjame Stroustrup最近說C++11就像一個新語言,的確,C++11核心已經(jīng)發(fā)生了巨大的變化,它現(xiàn)在支持Lambda表達式,對象類型自動推斷,統(tǒng)一的初始化語法,委托構(gòu)造函數(shù),deleted和defaulted函數(shù)聲明nullptr,以及最重要的右值引用。
【編輯推薦】
C++0x FAQ中文版(http://imcc.blogbus.com/logs/106046323.html)
C++11標準庫也使用新的算法,新的容器類,原子操作,類型特征,正則表達式,新的智能指針,async()函數(shù)和多線程庫進行了改造。
C++11的新內(nèi)核和庫特性完整列表請移步這里(http://www2.research.att.com/~bs/C++0xFAQ.html)。 C++標準在1998年獲得通過后,有兩位委員會委員預(yù)言,下一代C++標準將“肯定”包括內(nèi)置的垃圾回收器(GC),但可能不會支持多線程,因為定義一個可移植的線程模型涉及到的技術(shù)太復(fù)雜了,13年后,新的C++標準C++11也接近完成,你猜怎么著?讓那兩位委員沒想到的是,本次更新還是沒有包括GC,但卻包括了一個先進的線程庫。
在這篇文章中,我將介紹C++11標準中發(fā)生的***變化,以及為什么應(yīng)該引起注意,正如你將看到的,線程庫不是唯一的變化,新標準采納了數(shù)十位專家的意見,使C++變得更有意義。正如Rogers Cadenhead指出的那樣,它們就像迪斯科、寵物石和長胸毛的奧運游泳選手一樣不可思議。
首先,讓我們看看C++11核心語言的一些突出特性。
Lambda表達式
Lambda表達式允許你在本地定義函數(shù),即在調(diào)用的地方定義,從而消除函數(shù)對象產(chǎn)生的許多安全風(fēng)險,Lambda表達式的格式如下:
- [capture](parameters)->return-type {body}
[]里是函數(shù)調(diào)用的參數(shù)列表,表示一個Lambda表達式的開始,讓我們來看一個Lambda例子:
假設(shè)你想計算某個字符串包含多少個大寫字母,使用for_each()遍歷一個char數(shù)組,下面的Lambda表達式確定每個字母是否是大寫字母,每當(dāng)它發(fā)現(xiàn)一個大寫字母,Lambda表達式給Uppercase加1,Uppercase是定義在Lambda表達式外的一個變量:
- int main()
- {
- char s[]="Hello World!";
- int Uppercase = 0; //modified by the lambda
- for_each(s, s+sizeof(s), [&Uppercase] (char c) {
- if (isupper(c))
- Uppercase++;
- });
- cout<< Uppercase<<" uppercase letters in: "<< s<<endl;
- }
這是因為,如果你定義的函數(shù)主體被放置在另一個函數(shù)調(diào)用內(nèi)部,[&Uppercase]中的“&”記號意味著Lambda主體獲得一個Uppercase的引用,以便它能修改,如果沒有這個特殊記號,Uppercase將通過值傳遞,C++11 Lambda表達式也包括成員函數(shù)構(gòu)造器。
自動類型推斷和decltype
在C++03中,在聲明對象時,你必須指定對象的類型,然而,在許多情況下,對象的聲明包括在初始化代碼中,C++11利用了這個優(yōu)勢,允許你聲明對象時不指定類型:
- auto x=0; //x has type int because 0 is int
- auto c='a'; //char
- auto d=0.5; //double
- auto national_debt=14400000000000LL;//long long
相反,你可以聲明下面這樣的迭代器:
- void fucn(const vector<int> &vi)
- {
- vector<int>::const_iterator ci=vi.begin();
- }
關(guān)鍵字auto不是什么新生事物,我們早已認識,它實際上可以追溯到前ANSI C時代,但是,C++11改變了它的含義,auto不再指定自動存儲類型對象,相反,它聲明的對象類型是根據(jù)初始化代碼推斷而來的,C++11刪除了auto關(guān)鍵字的舊有含義以避免混淆,C++11提供了一個類似的機制捕捉對象或表達式的類型,新的操作符decltype需要一個表達式,并返回它的類型。
- const vector<int> vi;
- typedef decltype (vi.begin()) CIT;
- CIT another_const_iterator;
統(tǒng)一初始化語法
C++至少有4個不同的初始化符號,有些存在重疊,括號初始化語法如下:
- std::string s("hello");
- int m=int(); //default initialization
在某些情況下,你也可以使用“=”符號進行初始化:
- std::string s="hello";
- int x=5;
對于POD聚合,你還可以使用大括號:
- int arr[4]={0,1,2,3};
- struct tm today={0};
***,構(gòu)造函數(shù)使用成員進行初始化:
- struct S {
- int x;
- S(): x(0) {} };
顯然,這么多種初始化方法會引起混亂,對新手來說就更痛苦了,更糟糕的是,在C++03中,你不能初始化POD數(shù)組成員,POD數(shù)組使用new[]分配,C++11使用統(tǒng)一的大括號符號清理了這一混亂局面。
- class C
- {
- int a;
- int b;
- public:
- C(int i, int j);
- };
- C c {0,0}; //C++11 only. Equivalent to: C c(0,0);
- int* a = new int[3] { 1, 2, 0 }; /C++11 only
- class X {
- int a[4];
- public:
- X() : a{1,2,3,4} {} //C++11, member array initializer
- };
關(guān)于容器,你可以和一長串的push_back()調(diào)用說再見了,在C++11中,你可以直觀地初始化容器:
- // C++11 container initializer
- vector vs<string>={ "first", "second", "third"};
- map singers =
- { {"Lady Gaga", "+1 (212) 555-7890"},
- {"Beyonce Knowles", "+1 (212) 555-0987"}};
類似地,C++11支持類似的數(shù)據(jù)成員類內(nèi)初始化:
- class C
- {
- int a=7; //C++11 only
- public:
- C();
- };
#p#
Deleted和Defaulted函數(shù)
一個表單中的函數(shù):
- struct A
- {
- A()=default; //C++11
- virtual ~A()=default; //C++11
- };
被稱為一個defaulted函數(shù),“=default;”告訴編譯器為函數(shù)生成默認的實現(xiàn)。Defaulted函數(shù)有兩個好處:比手工實現(xiàn)更高效,讓程序員擺脫了手工定義這些函數(shù)的苦差事。
與defaulted函數(shù)相反的是deleted函數(shù):
- int func()=delete;
Deleted函數(shù)對防止對象復(fù)制很有用,回想一下C++自動為類聲明一個副本構(gòu)造函數(shù)和一個賦值操作符,要禁用復(fù)制,聲明這兩個特殊的成員函數(shù)=delete即可:
- struct NoCopy
- {
- NoCopy & operator =( const NoCopy & ) = delete;
- NoCopy ( const NoCopy & ) = delete;
- };
- NoCopy a;
- NoCopy b(a); //compilation error, copy ctor is deleted
nullptr
C++終于有一個關(guān)鍵字指定一個空指針常量了,nullptr取代了有錯誤傾向的null和文字0,這兩個被用來作為空指針替代品已經(jīng)有很多年的歷史了,nullptr是一個強類型:
- void f(int); //#1
- void f(char *);//#2
- //C++03
- f(0); //which f is called?
- //C++11
- f(nullptr) //unambiguous, calls #2
nullptr適用于所有指針類別,包括函數(shù)指針和成員指針:
- const char *pc=str.c_str(); //data pointers
- if (pc!=nullptr)
- cout<<pc<<endl;
- int (A::*pmf)()=nullptr; //pointer to member function
- void (*pmf)()=nullptr; //pointer to function
委托構(gòu)造函數(shù)
在C++11中,構(gòu)造函數(shù)可以調(diào)用相同類中的其它構(gòu)造函數(shù):
- class M //C++11 delegating constructors
- {
- int x, y;
- char *p;
- public:
- M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
- M(): M(0) {cout<<"delegating ctor"<
構(gòu)造函數(shù)#2,委托構(gòu)造函數(shù),調(diào)用目標構(gòu)造函數(shù)#1。
右值引用
C++03中的引用類型只能綁定左值,C++11引入了一種新型引用類型,叫做右值引用,右值引用可以綁定左值,例如,臨時對象和字面量。增加右值引用的主要原因是move(移動)語義,它和傳統(tǒng)的復(fù)制不一樣,移動意味著目標對象偷竊了源對象的資源,留下一個狀態(tài)為“空”的資源,在某些情況下,復(fù)制一個對象代價既高又沒有必要,可以用一個移動操作代替,如果你想評估移動帶來的性能收益,可以考慮字符串交換,一個幼稚的實現(xiàn)如下:
- void naiveswap(string &a, string & b)
- {
- string temp = a;
- a=b;
- b=temp;
- }
像這樣寫代價是很高的,復(fù)制字符串必須分配原始內(nèi)存,將字符從源位置復(fù)制到目標位置,相反,移動字符串僅僅是交換兩個數(shù)據(jù)成員,不用分配內(nèi)存,復(fù)制char數(shù)組和刪除內(nèi)存:
void moveswapstr(string& empty, string & filled)
{
//pseudo code, but you get the idea
size_t sz=empty.size();
const char *p= empty.data();
//move filled's resources to empty
empty.setsize(filled.size());
empty.setdata(filled.data());
//filled becomes empty
filled.setsize(sz);
filled.setdata(p);
}
如果你實現(xiàn)的類支持移動,你可以像下面這樣聲明一個移動構(gòu)造函數(shù)和一個移動賦值操作符:
- class Movable
- {
- Movable (Movable&&); //move constructor
- Movable&& operator=(Movable&&); //move assignment operator
- };
#p#
C++11標準庫廣泛使用了移動語義,許多算法和容器現(xiàn)在都為移動做了優(yōu)化。
C++11標準庫
C++于2003年以庫技術(shù)報告1(TR1)的形式經(jīng)歷了重大改版,TR1包括新的容器類(unordered_set,unordered_map,unordered_multiset和unordered_multimap)和多個支撐正則表達式、元組和函數(shù)對象封裝器等的新庫。隨著C++11標準獲得通過,TR1和自它***發(fā)布以來新增的庫被正式納入標準的C++標準,下面是C++11標準庫的一些特性:
線程庫
站在程序員的角度來看,C++11最重要的新功能毫無疑問是并行操作,C++11擁有一個代表執(zhí)行線程的線程類,在并行環(huán)境中用于同步,async()函數(shù)模板啟動并行任務(wù),為線程獨特的數(shù)據(jù)聲明thread_local存儲類型。如果你想找C++11線程庫的快速教程,請閱讀Anthony William的“C++0x中更簡單的多線程”。
新的智能指針類
C++98只定義了一個智能指針類auto_ptr,它現(xiàn)在已經(jīng)被廢棄了,C++11引入了新的智能指針類shared_ptr和最近添加的unique_ptr,兩者都兼容其它標準庫組件,因此你可以在標準容器內(nèi)安全保存這些智能指針,并使用標準算法操作它們。
新的算法
C++11標準庫定義了新的算法模仿all_of(),any_of()和none_of()操作,下面列出適用于ispositive()到(first, first+n)范圍,且使用all_of(), any_of() and none_of() 檢查范圍的屬性的謂詞:
- #include <algorithm>
- //C++11 code
- //are all of the elements positive?
- all_of(first, first+n, ispositive()); //false
- //is there at least one positive element?
- any_of(first, first+n, ispositive());//true
- // are none of the elements positive?
- none_of(first, first+n, ispositive()); //false
一種新型copy_n算法也可用了,使用copy_n()函數(shù),復(fù)制一個包含5個元素的數(shù)組到另一個數(shù)組的代碼如下:
- #include
- int source[5]={0,12,34,50,80};
- int target[5];
- //copy 5 elements from source to target
- copy_n(source,5,target);
算法iota()創(chuàng)建了一個值順序遞增的范圍,好像分配一個初始值給*first,然后使用前綴++使值遞增,在下面的代碼中,iota()分配連續(xù)值{10,11,12,13,14}給數(shù)組arr,并將{‘a’,’b’,’c’}分配給char數(shù)組c。
- include <numeric>
- int a[5]={0};
- char c[3]={0};
- iota(a, a+5, 10); //changes a to {10,11,12,13,14}
- iota(c, c+3, 'a'); //{'a','b','c'}
C++11仍然缺乏一些有用的庫,如XML API,套接字,GUI,反射以及前面提到的一個合適的自動垃圾回收器,但C++11的確也帶來了許多新特性,讓C++變得更加安全,高效,易學(xué)易用。
如果C++11的變化對你來說太大的話,也不要驚慌,多花些時間逐漸消化這一切,當(dāng)你完全吸收了C++11的變化后,你可能就會同意Stroustrup的說法:C++11感覺就像一個新語言,一個更好的新語言。
原文出處:http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/
原文名:The Biggest Changes in C++11
【編輯推薦】