最近在看C++ Primer5 恰好看到一半,總結一下C++11裏面確實加了不少新東西,若是沒有任何瞭解,別說本身寫了,看別人寫的代碼估計都會有些吃力。C++ Primer5是學習C++11的比較好的書籍。這篇文章僅總結關於C++11中的新東西,老的東西再也不贅述。本文的全部代碼僅僅值列出關鍵代碼,而且全部特性都已經用編譯器驗證過,個人編譯環境 gcc 5.3.1 g++ 5.3.1 ,聽說 4.7以上的版本已經支持大部分C++11的特性,VS系列的編譯器對C++11的支持狀況不甚瞭解,若是沒有合適的編譯器,能夠點擊這裏 C++shell 這是一個在線的C++編譯系統,裏面有多個選項能夠選擇C++98,C++11,C++14等,能夠在這裏面驗證C++11的正確性。html
long long 類型實際上沒有在C++ 98中存在,而以後被C99標準收錄,其實如今市面上大多數編譯器是支持 long long 的,可是這個類型正式成爲C++的標準類型是在C++11中。標準要求long long至少是64位也就是8個字節。一個字面常量使用LL後綴表示long long類型,使用ULL後綴表示unsigned long long 類型。程序員
C++11中全面加入了列表初始化的功能,包括對vector,map,值類型,struct等等均可以使用列表初始化,還能夠在函數中返回一個花括號括起來的列表,而在這以前咱們只能對數組進行列表初始化:shell
//數組列表初始化 int xx[5]={1,2,3,4,5}; int yy[]={6,7,8,9,0}; //值類型進行初始化 int a{10}; int b={10}; int c={10.123}; // 編譯器報錯,g++ 5.3.1當列表初始化用於值類型的時候,若是有精度損失,編譯器會報錯。 //列表初始化還能夠用結構體 typedef struct Str{ int x; int y; }Str; Str s = {10,20}; //列表初始化類,必須是public成員,若是含有私有成員會失敗 class Cls{ public: int x; int y; }; Cls c = {10,20}; //vector不只可使用列表初始化,還可使用列表進行賦值,數組不能用列表賦值 vector<int>v1={1,2,3,4,5,6,7,8,9}; // 初始化 vector<int>v2; v2={3,4,5,6,7}; //賦值 //map列表初始化 map<string ,int> m = { {"x",1}, {"y",2}, {"z",3} }; //用函數返回初始化列表只展現關鍵代碼,相關頭文件自行添加 //同理結構體,類,map的返回也可使用初始化列表返回 vector<int> getVector() { return {1,2,3,4,5}; } int main() { vector<int> v = getVector(); cout<<v[0]<<v[1]<<v.size()<<endl; return 0 ; }
C++11中新加入的字面值表示不指向任何對象的空指針,之前咱們經常用一個預約義的宏NULL來表示空指針,實際上NULL的值是0,新標準推薦使用nullptr而不是NULL數組
咱們在定義常量的時候通常使用const來定義,一個常量必須在定義的時候進行初始化,而且以後不可更改。一個常量必須使用一個常量表達式進行初始化,而且在編譯期間就能夠獲得常量的值,可是如何肯定一個表達式就是常量表達式呢,這個一般是由程序員本身肯定的,例如:安全
const int a =20; //20是一個字面值,固然也是一個常量表達式,因此用20來爲a賦值是沒有問題的 //然而下面的代碼也能夠經過編譯,g++ 5.3.1 int a = 20 ; const int x = a; int b[x]={0};
爲常量x賦值的是一個變量a,這樣作應該是不合理的,可是編譯器沒有報告任何錯誤,固然這種錯誤是顯而易見的,可是在複雜的系統中如何判斷一個表達式是不是常量表達式是很困難的,例如這裏的a咱們一眼就能夠判斷其並非一個常量表達式。爲此C++11提供了一個新的關鍵字constexpr,使用該關鍵字定義的常量,由編譯器檢查爲其賦值的表達式是不是常量表達式,例如上面的代碼改爲:函數
int a = 20 ; constexpr int x = a;
編譯器編譯的時候就會報錯說a並非常量。顯然constexpr關鍵字將常量表達式的檢查轉交給編譯器處理,而不是程序員本身,因此使用constexpr定義常量要比const安全。學習
普通的函數通常是不能用來爲constexpr常量賦值的,可是C++11容許定義一種constexpr的函數,這種函數在編譯期間就能夠計算出結果,這樣的函數是能夠用來爲constexpr賦值的。定義constexpr函數須要遵照一些約定,函數的返回類型以及全部形參的類型都應該是字面值,通常狀況下函數體中必須有且只有一條return語句。spa
constexpr int size() { return 42; } constexpr int si = size();
執行初始化的時候編譯器將函數的調用替換成結果值,constexpr函數體中也能夠出現除了return以外的其餘語句,可是這些語句在運行時不該該執行任何操做,例如空語句,using聲明等。constexpr函數容許其返回值並不是是一個字面值,例如:指針
constexpr int size(int s) { return s*4; } int a = 20; const int b = 30; constexpr int c = 40; constexpr int si = size(a); //error a是一個變量因此函數返回的是一個可變的值 constexpr int si1 = size(20); //ok 函數返回的其實是一個常量 constexpr int si2 = size(b); //ok constexpr int si3 = size(c); //ok
由上可知constexpr函數並不必定返回常量,若是應用於函數的參數是一個常量表達式則返回常量,不然返回變量,而該函數調用究竟是一個常量表達式仍是很是量表達式則由編譯器來判斷。這就是constexpr的好處。htm
類型別名其實早在C語言中就有了,通常狀況下咱們使用關鍵字typedef來聲明一個類型的別名,在C++11中增長了另外一種聲明類型別名的方法就是使用using關鍵字,using關鍵字在C++11之前通常用來引用命名空間。
typedef int INT; // 右側符號表明左側 using INT2 = int; // 左側符號表明右側 INT a = 20; INT2 b = 30;
咱們定義一個變量的時候首先必須肯定該變量的類型,而不少時候並非咱們先須要一個變量而後爲該變量賦值合適的數據,而是咱們有一個值可是咱們殊不知道該用什麼類型的變量存儲它,特別是C++的模版使用的很是普遍,有時候要定義一個變量,其類型是很複雜的會帶有模版的類型參數,例如一個最多見的例子:
map<string ,int> m ; map<string,int>::iterator it = m.begin();
上面的例子中咱們定義了一個map<string,int>::iterator類型的變量來存放 m.begin()的值,這個例子相對來講還不算困難可是我在開始使用map容器的時候也曾經被搞暈過,若是map是一個 map<string,double> 類型則須要定義一個 map<string.double>::iterator it 來存放了。特別是若是map和vector之間互相嵌套的狀況就更容易弄錯了。定義這種類型變量的另外一個缺點就是一個類型的名字每每會很長,試想一下程序代碼中通篇都是這種變量聲明,恐怕不會有幾我的看着舒服吧。不過不要緊C++11爲咱們定義了一個新的關鍵字 auto 用來定義變量,而變量的類型由編譯器自動根據賦值的表達式推導出來,不須要咱們顯示定義了。由於auto定義的變量的類型由編譯器根據賦值的表達式推導,因此auto定義的變量必須有初始值,不然編譯器無法肯定該變量的類型。
auto x = 20; // x 是int auto y = 3.14; // y 是double map<string ,int> m ; auto it = m.begin(); //it 是map<string,int>::iterator
這樣是否是方便了很多,並且程序看起來更加簡潔了
auto能夠在一條語句中聲明多個變量,可是要保證語句中的基礎數據類型只有一個,例如:
auto i=10,*p=&i; // OK i是int,p是int* auto a=10,b=3.14; // Error 類型是int仍是double ?
有時候會有這樣的需求,咱們須要知道一個表達式的類型,並使用該類型去定義一個變量,例如:
int a = 10; int b = 20; auto c = a + b; // OK a+b的類型是int,此時c的類型是int,而且c的值是 a+b
auto能夠解決部分問題,例如咱們定義的變量的類型就是表達式 a+b 的類型,可是若是咱們僅僅須要定義一個與表達式 a+b 的類型相同的變量,可是咱們又不但願將表達式a+b的值賦值給剛剛定義的變量,咱們但願賦另一個值或者是僅僅定義變量而不賦值呢。 這就須要用到C++11 提供的另外一個類型說明符 decltype了。decltype做用於一個表達式,而且返回該表達式的類型,在此過程當中編譯器分析表達式的類型,並不會計算表達式的值。例如
int a = 10; int b = 20; decltype(a+b) c = 50; // OK c的類型就是 a+b 的類型int
對於引用類型decltype有一些特別的地方:
int a = 20 ; int &b = a; decltype(b) c ; // Error c是引用類型必須賦值 decltype(b) d = a; // OK d是引用類型,指向a
能夠看到decltype若是做用於一個引用類型,其獲得的仍是一個引用類型。咱們知道一個引用類型在使用的時候通常會看成其關聯的那個變量的同義詞處理,例如若是使用 cout<<b<<endl; 其中b實際上至關於a,可是decltype做用於引用類型的時候會保留引用性質。
若是一個表達式是一個解指針引用的操做,decltype獲得的也是一個引用類型:
int a = 20 ; int *p = &a; decltype(*p) c = a; // c的類型是int& c = 50; cout<<a<<endl; // 輸出50
當decltype做用於一個變量的時候,變量加不加括號是有區別的,例如:
int a = 20; decltype(a) b = 30; //ok b的類型是 int decltype((a)) c = a ; // ok c的類型是int& 其關聯變量 a
加上括號以後編譯器會把(a)看成是一個表達式處理,而變量是一種能夠做爲賦值語句左值的表達式,因此會解釋成引用類型。
若是您以爲這篇文章對您有幫助,須要您的【贊】,讓更多的人也能看見哦