李亮
審稿人:徐方友,邱帥
說起C++語言,它現在被公認為是在各種編程語言中最難學的語言之一,它的語法知識點之廣泛,細節內容之多,學習難度之大,時間周期之長,都使得人們對這門語言“敬而遠之”。然而C++語言又具有不可替代的巨大優勢。主要的優勢有以下幾點,首先C++是一個嚴謹,精確的語言。它的標準定義非常明確,語言結構層次分明,清晰順暢,具有優秀的編程語言應該具有的品質。其次,C++語言支持對底層的操作,這是又一大優勢。支持底層操作意味著編程的效率將顯著提升。C++語言在繼承c語言的基礎上,改進了自己對底層的支持,這令其更加有吸引力。第三,C++語言完全兼容c語言,這使得人們可以按照傳統C的面向過程式的編程,這對于實現底層和內核操作無疑是一大亮點。第四,C++語言是面向對象編程的程序語言,它不僅僅是對c語言的擴充,它在面向對象方面新增了模板,STL,以及各類容器,使得它更加適合大型軟件工程的開發和創新。另外,C++所獨有的模板元編程的能力也是它繼續保持生命力的關鍵所在。最后,C++語言具有極高的性能,這是C++語言眾多優勢中的核心,這種優勢使它廣泛應用于大型服務器編程,游戲開發,云計算等高端技術領域。如今,C++語言仍然應用在各個領域,在11月的TIOBE編程語言排行榜中,C++語言仍然穩居前三,這是對C++語言優勢最好的證明。
C++語言是在不斷發展和改進的,正如C++之父本賈尼所說,c++好像是一種全新的編程語言。C++11是C++程序設計語言標準的一個新的版本,在2011年由ISO批準并發布。C++11新標準從而代替了原來的C++98和C++03.。C++11標準是對C++的一次巨大的改進和擴充。在核心語法,STL標準模板等方面增加眾多新功能,新亮點。例如新增auto,deltype,nullptr等關鍵字,增加范圍for循環,新增lambda表達式等。下面將對C++增加的眾多新特性進行總結。
(一)auto關鍵字
C++11新標準成功引入了auto關鍵字,auto關鍵字可以幫助我們分析表達式所屬的類型。和以前的一些類型說明符明顯不同的是,auto關鍵字可以讓編譯器自動分析某個初始值來判斷它所屬的類型。當然,使用auto關鍵字必須確定初始值。
如圖1所示,我們定義了一個變量a,它的初始值是一個make_share類型。這樣我們就新建了一個指向string的智能指針shared_ptr;相比之前的C++標準,這樣將會更加簡便,利于我們快速完成程序設計。
(二)decltype關鍵字
C++11新標準引入了又一種類型說明符decltype,它的功能是選擇并返回操作數的數據類型。它會使得編譯器自動分析表達式的類型并得到它的類型,最關鍵是它不會去計算表達式的值。例如圖2所示:
我們定義了一個常量b,它的初值是10,又定義了一個值為20的變量c,然后通過decltype關鍵字定義了兩個變量x,y。讓編譯器通過推斷括號里的表達式來判斷x,y的類型。我們很容易得出b的類型是const int,而c的類型是int。
(三)字面值nullptr
我們都知道所謂空指針就是不指向任何對象的指針,定義空指針的辦法有很多。例如將這個指針的初值設為0,或者將它的初值設為NULL,在標準庫iostream中,NULL代表的就是0.
而C++11新標準引入了一個新的字面值來初始化空指針,它就是nullptr,nullptr是一個比較特殊的字面值,它可以任意轉換成其他的任意指針類型。例如圖3所示:
如圖3所示,我們定義了一個指向int的指針p,且把它用字面值nullptr初始化,隨后定義了一個指向動態內存的指針q,然后將q所指向的動態內存地址賦給p,現在p就指向了那個地址,我們釋放q。
(四)constexpr關鍵字
C++新標準引入constexpr關鍵字,它許可將變量聲明為constexpr來由編譯器驗證變量是否是一個常量表達式。當然,聲明為constexpr的變量一定是一個常量表達式。而且需要用長量表達式初始化。例如:
如圖4所示。我們定義了常量val,并給它賦予了初值,然后用val+1來初始化另外一個常量val2.這里要說明的是如果肯定一個變量是常量表達式,我們就可以把它聲明為constexpr類型。
(五)范圍for語句
范圍for語句是c++11新標準一個重要的引用,這種遍歷語句遍歷指定序列的每個元素,并且可以對每個元素進行某種操作。它的語法格式是:
For(declaration : expression)
statement;
其中,declaration是一個變量,它用于表示一個原子元素或者基礎元素,而expression是一個對象或者是一個序列,是被遍歷的對象。這個對象將被訪問里面的原子元素。訪問一個元素之后,將會被推進至下一個需要被訪問的值。例如圖5所示:
如圖5所示。我們定義了一個vector容器ivec,并且對它進行了列表初始化(列表初始化也是c++11新標準引入的),運用范圍for循環,遍歷ivec容器里面的元素,這里的c可被編譯器推斷為string類型,auto關鍵字的作用前面已說明,不在詳述。我們每遍歷一個容器中的元素就將它賦值為“b”,然后將它輸出,這也證明了可用范圍for語句對容器或者對象中的基礎元素進行指定操作。
(六)Lambda表達式
C++11新標準新增的一項重要功能就是lambda表達式,所謂lambda就是表示一個可調用的代碼單元,也可以說是一個可調用對象,還可以理解為一個沒有命名的內聯函數。Lambda的組成結構與函數很相似,它擁有一個返回類型,一個形參列表,一個函數體。Lambda也可以定義在函數內部。它的組成結構如下:
[capture list] (parameter list) -> return type { function body}
Capture list 表示捕獲列表,也就是lambda所在函數中的局部變量的列表,如果沒有,則這個列表為空。Return type 表示該lambda的返回類型,Parameter list 為形參列表,Function body是函數體,這些和函數表示是一樣的。需要注意的是,如果有返回類型,lambda必須使用尾置返回來確定類型。另外,lambda必須包括捕獲列表和函數體。另外的幾個可以省略。例如:
如圖6所示。我們定義了一個名為fcn的lambda表達式。它只有捕獲列表和函數體。這個lambda表達式返回值為1.之后。我們調用這個lambda,會輸出1。
(七)initializer_list
Initializer_list定義在c++11新標準新引入的initializer_list頭文件中。Initializer_list也是一種標準庫類型。此類型用于訪問c++初始化列表中的值,列表中的元素類型為const的,也就是常量,我們無法改變initializer_list中的元素的值。這種類型的對象由編譯器從初始化列表聲明中直接自動構造,所謂初始化列表聲明就是被包括在花括號里面的,用逗號分隔元素的列表。Initializer_list可以用于表示某種特定類型的值的數組。
如圖7所示,我們定義了一個名為mylist的類,它有一個initializer_list類型的構造函數,此構造函數將傳遞進來的initializer_list的類型參數中的值存入str中,并以空格分隔。
(八)標準庫bind函數
C++11引入了新的標準庫函數bind,我們可以將bind看成一個函數適配器。它的組成結構如下:
Auto newcallable = bind(callable,arg_list)
Newcallable表示一個新的可調用對象,arg_list表示參數列表,這個參數也就是callable所對應的的參數。當我們調用newcallable時,newcallable會調用callable,并且把arg_list中的參數傳遞給callable。對于arg_list,這個燦叔列表可能是具有實際值得參數,也可能是像_n這樣的占位符。這個占位符表示newcallable的參數。比如_1就是newcallable的第一個參數,_2就是newcallable的第二個參數。以此類推。如圖8:
在圖8中,我們定義了一個名為myfun的類。它有3個成員,分別是a,b兩個int型的成員,和一個有加法功能的成員函數。注意,我們的占位符是定義在std命名空間中的placeholders中。首先定義了一個名為fcn的新調用對象,隨后我們調用標準庫函數bind。它的調用對象,也就是它的第一個參數是成員函數add_num,我們把參數1,3,傳遞給這個成員函數。最后,我們調用fcn這個可調用對象。得到結果4.
我們再定義了一個名為fcn2的可調用對象,它同樣以myfun作為可調用參數。我們這里使用了占位符,它表示fcn2具有一個參數。當我調用fcn2時,我們將這兩個參數傳遞給myfun。隨后輸出結果。
(九)智能指針shared_ptr,unique_ptr
新的C++11標準庫為了更好地管理和使用動態內存,防止內存泄漏和懸垂指針,定義了兩個智能指針類型,分別是shared_ptr和unique_ptr。它們均定義在頭文件memory中。Shared_ptr可以讓多個智能指針指向同一個對象,而unique_ptr只容許一個指針獨自指向一個對象。下面詳細說明這兩個智能指針。
Shared_ptr是用于管理動態內存的指針,它擁有垃圾回收機制。可與其他對象共享管理,這種指針具有取得指針所有權并共享該所有權的能力。當最后一個指針釋放所有權時,也就是引用計數為0時,指針的所有者將刪除該指針。Shared_ptr自身如果被銷毀,或者通過賦值或者通過調用reset函數,他們的值被改變的話,它就釋放對共享對象的所有權。
Unique_ptr擁有它所指向的對象,它與shared_ptr不同點是,在任一時刻,只能有一個unique_ptr指向一個對象。如果這個unique_ptr被銷毀,則它所指向的對象也要被銷毀。
例如:
如圖9所示,我們定義了5個智能指針,首先定義的ptr是一個shared_ptr類型,他通過使用make_shared來初始化。之后的4個指針都是unique_ptr,其中,ptr4是一個管理動態int對象的指針,ptr3接管了ptr4所指向的對象。在語句中。我們看到ptr4調用release函數解除自己對所指對象的所有權,并將它賦給ptr3.ptr5同樣的道理。通過調用reset函數。釋放它原來擁有的對象,接管ptr3所指的對象。
(十)右值引用
C++11標準庫引入了右值引用,這是一種全新的引用類型,之所以稱為右值引用,因為它就是必須綁定在右值的引用上。右值引用的符號是&&,而不是&。右值引用擁有一條關鍵的性質。那就是它只能綁定在一個將要銷毀的對象上。所以它更加方便移動操作。
我們可以將右值引用進行分類。首先一類右值引用是將亡值,也就是馬上要銷毀的值,一般指的是跟右值引用相關的表達式,這樣的表達式是要被銷毀的對象。另外一類是純右值,
例如按值返回的臨時對象,運算表達式產生的臨時對象,原始字面值,lambda等。
上述總結了C++11十大典型的新特性。通過對c++11新特性的應用,可以更好地理解和掌握c++語言。C++11還有很多新特性,在此不再舉例詳述。具體請參考《C++ primer 5》,《深入理解C++11》,《深入探索c++對象模型》。