static_assert提供一個編譯時的斷言檢查。若是斷言爲真,什麼也不會發生。若是斷言爲假,編譯器會打印一個特殊的錯誤信息。程序員
static_assert和type traits一塊兒使用能發揮更大的威力。type traits是一些class,在編譯時提供關於類型的信息。在頭文件<type_traits>中能夠找到它們。這個頭文件中有好幾種 class: helper class,用來產生編譯時常量。type traits class,用來在編譯時獲取類型信息,還有就是type transformation class,他們能夠將已存在的類型變換爲新的類型。算法
下面這段代碼本來指望只作用於整數類型。數組
可是若是有人寫出以下代碼,編譯器並不會報錯安全
程序會打印出4.14和」e」。可是若是咱們加上編譯時斷言,那麼以上兩行將產生編譯錯誤。多線程
Move semantics (Move語義)async
這是C++11中所涵蓋的另外一個重要話題。就這個話題能夠寫出一系列文章,僅用一個段落來講明顯然是不夠的。所以在這裏我不會過多的深刻細節,若是你還不是很熟悉這個話題,我鼓勵你去閱讀更多地資料。函數
C++11加入了右值引用(rvalue reference)的概念(用&&標識),用來區分對左值和右值的引用。左值就是一個有名字的對象,而右值則是一個無名對象(臨時對 象)。move語義容許修改右值(之前右值被看做是不可修改的,等同於const T&類型)。學習
C++的class或者struct之前都有一些隱含的成員函數:默認構造函數(僅當沒有顯示定義任何其餘構造函數時才存在),拷貝構造函數,析構 函數還有拷貝賦值操做符。拷貝構造函數和拷貝賦值操做符提供bit-wise的拷貝(淺拷貝),也就是逐個bit拷貝對象。也就是說,若是你有一個類包含 指向其餘對象的指針,拷貝時只會拷貝指針的值而不會管指向的對象。在某些狀況下這種作法是沒問題的,但在不少狀況下,實際上你須要的是深拷貝,也就是說你 但願拷貝指針所指向的對象。而不是拷貝指針的值。這種狀況下,你須要顯示地提供拷貝構造函數與拷貝賦值操做符來進行深拷貝。this
若是你用來初始化或拷貝的源對象是個右值(臨時對象)會怎麼樣呢?你仍然須要拷貝它的值,但隨後很快右值就會被釋放。這意味着產生了額外的操做開銷,包括本來並不須要的空間分配以及內存拷貝。spa
如今說說move constructor和move assignment operator。這兩個函數接收T&&類型的參數,也就是一個右值。在這種狀況下,它們能夠修改右值對象,例如「偷走」它們內部指針所 指向的對象。舉個例子,一個容器的實現(例如vector或者queue)可能包含一個指向元素數組的指針。當用一個臨時對象初始化一個對象時,咱們不需 要分配另外一個數組,從臨時對象中把值複製過來,而後在臨時對象析構時釋放它的內存。咱們只須要將指向數組內存的指針值複製過來,由此節約了一次內存分配, 一次元數組的複製以及後來的內存釋放。
如下代碼實現了一個簡易的buffer。這個buffer有一個成員記錄buffer名稱(爲了便於如下的說明),一個指針(封裝在unique_ptr中)指向元素爲T類型的數組,還有一個記錄數組長度的變量。
默認的copy constructor以及copy assignment operator你們應該很熟悉了。C++11中新增的是move constructor以及move assignment operator,這兩個函數根據上文所描述的move語義實現。若是你運行這段代碼,你就會發現b4構造時,move constructor會被調用。一樣,對b1賦值時,move assignment operator會被調用。緣由就在於getBuffer()的返回值是一個臨時對象——也就是右值。
你也許注意到了,move constuctor中當咱們初始化變量name和指向buffer的指針時,咱們使用了std::move。name其實是一個 string,std::string實現了move語義。std::unique_ptr也同樣。可是若是咱們寫_name(temp._name), 那麼copy constructor將會被調用。不過對於_buffer來講不能這麼寫,由於std::unique_ptr沒有copy constructor。但爲何std::string的move constructor此時沒有被調到呢?這是由於雖然咱們使用一個右值調用了Buffer的move constructor,但在這個構造函數內,它其實是個左值。爲何?由於它是有名字的——「temp」。一個有名字的對象就是左值。爲了再把它變爲 右值(以便調用move constructor)必須使用std::move。這個函數僅僅是把一個左值引用變爲一個右值引用。
更新:雖然這個例子是爲了說明如何實現move constructor以及move assignment operator,但具體的實現方式並非惟一的。在本文的回覆中Member 7805758同窗提供了另外一種可能的實現。爲了方便查看,我把它也列在下面:
結論
關於C++11還有不少要說的。本文只是各類入門介紹中的一個。本文展現了一系列C++開發者應當使用的核心語言特性與標準庫函數。然而我建議你能更加深刻地學習,至少也要再看看本文所介紹的特性中的部分。
Deleted和Defaulted函數
一個表單中的函數:
被稱爲一個defaulted函數,「=default;」告訴編譯器爲函數生成默認的實現。Defaulted函數有兩個好處:比手工實現更高效,讓程序員擺脫了手工定義這些函數的苦差事。
與defaulted函數相反的是deleted函數:
Deleted函數對防止對象複製頗有用,回想一下C++自動爲類聲明一個副本構造函數和一個賦值操做符,要禁用複製,聲明這兩個特殊的成員函數=delete便可:
委託構造函數
在C++11中,構造函數能夠調用相同類中的其它構造函數:
構造函數#2,委託構造函數,調用目標構造函數#1。
線程庫
站在程序員的角度來看,C++11最重要的新功能毫無疑問是並行操做,C++11擁有一個表明執行線程的線程類,在並行環境中用於同步,async()函數模板啓動並行任務,爲線程獨特的數據聲明thread_local存儲類型。若是你想找C++11線程庫的快速教程,請閱讀Anthony William的「C++0x中更簡單的多線程」。
新的算法
C++11標準庫定義了新的算法模仿all_of(),any_of()和none_of()操做,下面列出適用於ispositive()到(first, first+n)範圍,且使用all_of(), any_of() and none_of() 檢查範圍的屬性的謂詞:
一種新型copy_n算法也可用了,使用copy_n()函數,複製一個包含5個元素的數組到另外一個數組的代碼以下:
算法iota()建立了一個值順序遞增的範圍,好像分配一個初始值給*first,而後使用前綴++使值遞增,在下面的代碼中,iota()分配連續值{10,11,12,13,14}給數組arr,並將{‘a’,’b’,’c’}分配給char數組c。
C++11仍然缺少一些有用的庫,如XML API,套接字,GUI,反射以及前面提到的一個合適的自動垃圾回收器,但C++11的確也帶來了許多新特性,讓C++變得更加安全,高效,易學易用。
若是C++11的變化對你來講太大的話,也不要驚慌,多花些時間逐漸消化這一切,當你徹底吸取了C++11的變化後,你可能就會贊成Stroustrup的說法:C++11感受就像一個新語言,一個更好的新語言。
咱們在C++中都用過pair,pair可使用make_pair構造,構造一個包含兩種不一樣類型的數據的容器。好比,以下代碼:
auto p = make_pair(1, "C++ 11");
因爲在C++11中引入了變長參數模板,因此發明了新的數據類型:tuple,tuple是一個N元組,能夠傳入1個, 2個甚至多個不一樣類型的數據
auto t1 = make_tuple(1, 2.0, "C++ 11"); auto t2 = make_tuple(1, 2.0, "C++ 11", {1, 0, 2});
這樣就避免了從前的pair中嵌套pair的醜陋作法,使得代碼更加整潔
另外一個常常見到的例子是Print函數,在C語言中printf能夠傳入多個參數,在C++11中,咱們能夠用變長參數模板實現更簡潔的Print
template<typename head, typename... tail> void Print(Head head, typename... tail) { cout<< head <<endl; Print(tail...); }