C++11的新特性簡單匯總介紹 (一)
什么是C++11
C++11是曾經(jīng)被叫做C++0x,是對目前C++語言的擴(kuò)展和修正,C++11不僅包含核心語言的新機(jī)能,而且擴(kuò)展了C++的標(biāo)準(zhǔn)程序庫(STL),并入了大部分的C++ Technical Report 1(TR1)程序庫(數(shù)學(xué)的特殊函數(shù)除外)。
C++11包括大量的新特性:包括lambda表達(dá)式,類型推導(dǎo)關(guān)鍵字auto、decltype,和模板的大量改進(jìn)。
1. 概述
最近在看C++ Primer5
剛好看到一半,總結(jié)一下C++11里面確實(shí)加了很多新東西,如果沒有任何了解,別說自己寫了,看別人寫的代碼估計(jì)都會有些吃力。C++ Primer5
是學(xué)習(xí)C++11的比較好的書籍。這篇文章僅總結(jié)關(guān)于C++11中的新東西,老的東西不再贅述。本文的所有代碼僅僅值列出關(guān)鍵代碼,并且所有特性都已經(jīng)用編譯器驗(yàn)證過,我的編譯環(huán)境 gcc 5.3.1 g++ 5.3.1 ,據(jù)說 4.7以上的版本已經(jīng)支持大部分C++11的特性,VS系列的編譯器對C++11的支持情況不甚了解,如果沒有合適的編譯器,可以點(diǎn)擊這里 C++shell 這是一個(gè)在線的C++編譯系統(tǒng),里面有多個(gè)選項(xiàng)可以選擇C++98,C++11,C++14等,可以在這里面驗(yàn)證C++11的正確性。
2. long long 類型
long long
類型實(shí)際上沒有在C++ 98中存在,而之后被C99標(biāo)準(zhǔn)收錄,其實(shí)現(xiàn)在市面上大多數(shù)編譯器是支持 long long 的,但是這個(gè)類型正式成為C++的標(biāo)準(zhǔn)類型是在C++11中。標(biāo)準(zhǔn)要求long long至少是64位也就是8個(gè)字節(jié)。一個(gè)字面常量使用LL后綴表示long long類型,使用ULL后綴表示unsigned long long
類型。
3. 列表初始化
C++11中全面加入了列表初始化的功能,包括對vector,map,值類型,struct等等都可以使用列表初始化,還可以在函數(shù)中返回一個(gè)花括號括起來的列表,而在這之前我們只能對數(shù)組進(jìn)行列表初始化:
//數(shù)組列表初始化 int xx[5]={1,2,3,4,5}; int yy[]={6,7,8,9,0}; //值類型進(jìn)行初始化 int a{10}; int b={10}; int c={10.123}; // 編譯器報(bào)錯,g++ 5.3.1當(dāng)列表初始化用于值類型的時(shí)候,如果有精度損失,編譯器會報(bào)錯。 //列表初始化還可以用結(jié)構(gòu)體 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不僅可以使用列表初始化,還可以使用列表進(jìn)行賦值,數(shù)組不能用列表賦值 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} }; //用函數(shù)返回初始化列表只展示關(guān)鍵代碼,相關(guān)頭文件自行添加 //同理結(jié)構(gòu)體,類,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 ; }
4. nullptr 空指針
C++11中新加入的字面值表示不指向任何對象的空指針,以前我們常常用一個(gè)預(yù)定義的宏NULL來表示空指針,實(shí)際上NULL的值是0,新標(biāo)準(zhǔn)推薦使用nullptr而不是NULL
5. constexpr變量
我們在定義常量的時(shí)候一般使用const來定義,一個(gè)常量必須在定義的時(shí)候進(jìn)行初始化,并且之后不可更改。一個(gè)常量必須使用一個(gè)常量表達(dá)式進(jìn)行初始化,并且在編譯期間就可以得到常量的值,但是如何確定一個(gè)表達(dá)式就是常量表達(dá)式呢,這個(gè)通常是由程序員自己確定的,例如:
const int a =20; //20是一個(gè)字面值,當(dāng)然也是一個(gè)常量表達(dá)式,所以用20來為a賦值是沒有問題的 //然而下面的代碼也可以通過編譯,g++ 5.3.1 int a = 20 ; const int x = a; int b[x]={0};
為常量x賦值的是一個(gè)變量a,這樣做應(yīng)該是不合理的,但是編譯器沒有報(bào)告任何錯誤,當(dāng)然這種錯誤是顯而易見的,但是在復(fù)雜的系統(tǒng)中如何判斷一個(gè)表達(dá)式是否是常量表達(dá)式是很困難的,例如這里的a我們一眼就可以判斷其并不是一個(gè)常量表達(dá)式。為此C++11提供了一個(gè)新的關(guān)鍵字constexpr,使用該關(guān)鍵字定義的常量,由編譯器檢查為其賦值的表達(dá)式是否是常量表達(dá)式,例如上面的代碼改成:
int a = 20 ; constexpr int x = a;
編譯器編譯的時(shí)候就會報(bào)錯說a并不是常量。顯然constexpr關(guān)鍵字將常量表達(dá)式的檢查轉(zhuǎn)交給編譯器處理,而不是程序員自己,所以使用constexpr定義常量要比const安全。
6. constexpr函數(shù)
普通的函數(shù)一般是不能用來為constexpr常量賦值的,但是C++11允許定義一種constexpr的函數(shù),這種函數(shù)在編譯期間就可以計(jì)算出結(jié)果,這樣的函數(shù)是可以用來為constexpr賦值的。定義constexpr函數(shù)需要遵守一些約定,函數(shù)的返回類型以及所有形參的類型都應(yīng)該是字面值,一般情況下函數(shù)體中必須有且只有一條return語句。
constexpr int size() { return 42; } constexpr int si = size();
執(zhí)行初始化的時(shí)候編譯器將函數(shù)的調(diào)用替換成結(jié)果值,constexpr函數(shù)體中也可以出現(xiàn)除了return之外的其他語句,但是這些語句在運(yùn)行時(shí)不應(yīng)該執(zhí)行任何操作,例如空語句,using聲明等。constexpr函數(shù)允許其返回值并非是一個(gè)字面值,例如:
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是一個(gè)變量所以函數(shù)返回的是一個(gè)可變的值 constexpr int si1 = size(20); //ok 函數(shù)返回的實(shí)際上是一個(gè)常量 constexpr int si2 = size(b); //ok constexpr int si3 = size(c); //ok
由上可知constexpr函數(shù)并不一定返回常量,如果應(yīng)用于函數(shù)的參數(shù)是一個(gè)常量表達(dá)式則返回常量,否則返回變量,而該函數(shù)調(diào)用到底是一個(gè)常量表達(dá)式還是非常量表達(dá)式則由編譯器來判斷。這就是constexpr的好處。
7. using類型別名
類型別名其實(shí)早在C語言中就有了,一般情況下我們使用關(guān)鍵字typedef來聲明一個(gè)類型的別名,在C++11中增加了另一種聲明類型別名的方法就是使用using關(guān)鍵字,using關(guān)鍵字在C++11以前一般用來引用命名空間。
typedef int INT; // 右側(cè)符號代表左側(cè) using INT2 = int; // 左側(cè)符號代表右側(cè) INT a = 20; INT2 b = 30;
8. auto類型指示符
我們定義一個(gè)變量的時(shí)候首先必須確定該變量的類型,而很多時(shí)候并不是我們先需要一個(gè)變量然后為該變量賦值合適的數(shù)據(jù),而是我們有一個(gè)值但是我們卻不知道該用什么類型的變量存儲它,特別是C++的模版使用的非常廣泛,有時(shí)候要定義一個(gè)變量,其類型是很復(fù)雜的會帶有模版的類型參數(shù),例如一個(gè)最常見的例子:
map<string ,int> m ; map<string,int>::iterator it = m.begin();
上面的例子中我們定義了一個(gè)map<string,int>::iterator類型的變量來存放 m.begin()的值,這個(gè)例子相對來說還不算困難但是我在開始使用map容器的時(shí)候也曾經(jīng)被搞暈過,如果map是一個(gè) map<string,double> 類型則需要定義一個(gè) map<string.double>::iterator it 來存放了。特別是如果map和vector之間互相嵌套的情況就更容易弄錯了。定義這種類型變量的另一個(gè)缺點(diǎn)就是一個(gè)類型的名字往往會很長,試想一下程序代碼中通篇都是這種變量聲明,恐怕不會有幾個(gè)人看著舒服吧。不過沒關(guān)系C++11為我們定義了一個(gè)新的關(guān)鍵字 auto 用來定義變量,而變量的類型由編譯器自動根據(jù)賦值的表達(dá)式推導(dǎo)出來,不需要我們顯示定義了。因?yàn)閍uto定義的變量的類型由編譯器根據(jù)賦值的表達(dá)式推導(dǎo),所以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可以在一條語句中聲明多個(gè)變量,但是要保證語句中的基礎(chǔ)數(shù)據(jù)類型只有一個(gè),例如:
auto i=10,*p=&i; // OK i是int,p是int* auto a=10,b=3.14; // Error 類型是int還是double ?
9. decltype類型指示符
有時(shí)候會有這樣的需求,我們需要知道一個(gè)表達(dá)式的類型,并使用該類型去定義一個(gè)變量,例如:
int a = 10; int b = 20; auto c = a + b; // OK a+b的類型是int,此時(shí)c的類型是int,并且c的值是 a+b
auto可以解決部分問題,例如我們定義的變量的類型就是表達(dá)式 a+b 的類型,但是如果我們僅僅需要定義一個(gè)與表達(dá)式 a+b 的類型相同的變量,但是我們又不希望將表達(dá)式a+b的值賦值給剛剛定義的變量,我們希望賦另外一個(gè)值或者是僅僅定義變量而不賦值呢。 這就需要用到C++11 提供的另一個(gè)類型說明符 decltype了。decltype作用于一個(gè)表達(dá)式,并且返回該表達(dá)式的類型,在此過程中編譯器分析表達(dá)式的類型,并不會計(jì)算表達(dá)式的值。例如
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如果作用于一個(gè)引用類型,其得到的還是一個(gè)引用類型。我們知道一個(gè)引用類型在使用的時(shí)候一般會當(dāng)作其關(guān)聯(lián)的那個(gè)變量的同義詞處理,例如如果使用 cout<<b<<endl; 其中b實(shí)際上相當(dāng)于a,但是decltype作用于引用類型的時(shí)候會保留引用性質(zhì)。
如果一個(gè)表達(dá)式是一個(gè)解指針引用的操作,decltype得到的也是一個(gè)引用類型:
int a = 20 ; int *p = &a; decltype(*p) c = a; // c的類型是int& c = 50; cout<<a<<endl; // 輸出50
當(dāng)decltype作用于一個(gè)變量的時(shí)候,變量加不加括號是有區(qū)別的,例如:
int a = 20; decltype(a) b = 30; //ok b的類型是 int decltype((a)) c = a ; // ok c的類型是int& 其關(guān)聯(lián)變量 a
加上括號之后編譯器會把(a)當(dāng)作是一個(gè)表達(dá)式處理,而變量是一種可以作為賦值語句左值的表達(dá)式,所以會解釋成引用類型。
相關(guān)文章
在C/C++與Python之間實(shí)現(xiàn)通信的常見方法
在C/C++與Python之間實(shí)現(xiàn)通信的方式有很多,本文給大家介紹了一些常見的方法,文中通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-12-12C語言實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04

C語言中main函數(shù)與命令行參數(shù)詳細(xì)講解

C語言中結(jié)構(gòu)體和共用體實(shí)例教程