C++11 新功能
文章目錄
- C++11
- 一 列表初始化
- 單個(gè)對(duì)象的多參數(shù)列表初始化
- 多個(gè)對(duì)象的列表初始化
- 自己編寫list使用initializer_list支持多對(duì)象的列表初始化
- 二 stl中的一些變化
- array
- forword_list
- unordered_map unordered_set
- 三 右值引用和移動(dòng)語義
- 左值引用& vs 右值引用&&
- 右值引用的應(yīng)用
- 移動(dòng)構(gòu)造
- 移動(dòng)賦值
- 完美轉(zhuǎn)發(fā)
- 使用完美轉(zhuǎn)發(fā)之后:
- emplace_back()
- 類的新功能
- 類成員初始化
- 四 可調(diào)用對(duì)象類型
- lambda表達(dá)式
- lambda表達(dá)式各部分說明
- 捕捉聲明:
- 注意事項(xiàng):
- 函數(shù)對(duì)象(仿函數(shù))與lambda表達(dá)式
- 五 關(guān)鍵字
- auto
- decltype
- final-不讓繼承
- override-檢查能否被重寫
- default
- delete
- 六 模板的可變參數(shù)
- 逗號(hào)表達(dá)式展開參數(shù)包
C++11
在2003年C++標(biāo)準(zhǔn)委員會(huì)曾經(jīng)提交了一份技術(shù)勘誤表(簡稱TC1),使得C++03這個(gè)名字已經(jīng)取代了C++98稱為
C++11之前的最新C++標(biāo)準(zhǔn)名稱。不過由于TC1主要是對(duì)C++98標(biāo)準(zhǔn)中的漏洞進(jìn)行修復(fù),語言的核心部分則沒
有改動(dòng),因此人們習(xí)慣性的把兩個(gè)標(biāo)準(zhǔn)合并稱為C++98/03標(biāo)準(zhǔn)。從C++0x到C++11,C++標(biāo)準(zhǔn)10年磨一劍,
第二個(gè)真正意義上的標(biāo)準(zhǔn)珊珊來遲。相比于C++98/03,C++11則帶來了數(shù)量可觀的變化,其中包含了約140
個(gè)新特性,以及對(duì)C++03標(biāo)準(zhǔn)中約600個(gè)缺陷的修正,這使得C++11更像是從C++98/03中孕育出的一種新語
言。相比較而言,C++11能更好地用于系統(tǒng)開發(fā)和庫開發(fā)、語法更加泛華和簡單化、更加穩(wěn)定和安全,不僅功能更強(qiáng)大,而且能提升程序員的開發(fā)效率。
一 列表初始化
在C++98中,標(biāo)準(zhǔn)允許使用花括號(hào){}對(duì)數(shù)組元素進(jìn)行統(tǒng)一的列表初始值設(shè)定。比如:
int array1[] = {1,2,3,4,5};int array2[5] = {0};
對(duì)于一些自定義的類型,卻無法使用這樣的初始化。比如:
vector<int> v{1,2,3,4,5};就無法通過編譯,導(dǎo)致每次定義vector時(shí),都需要先把vector定義出來,然后使用循環(huán)對(duì)其賦初始值,非常不方便。C++11擴(kuò)大了用大括號(hào)括起的列表(初始化列表)的使用范圍,使其可用于所有的內(nèi)置類型和用戶自定義的類型,使用初始化列表時(shí),可添加等號(hào)(=)也可不添加。列表初始化可以在{}之前使用等號(hào),其效果與不使用=沒有什么區(qū)別。
單個(gè)對(duì)象的多參數(shù)列表初始化
#include<iostream> #include<vector> #include<map> #include<string> using namespace std; struct B {int _x;int _y; }; class Point { public:Point(int x = 0, int y = 0):_x(x), _y(y){} private:int _x;int _y; }; class A { public://內(nèi)含單參數(shù)的構(gòu)造函數(shù)A(int a):_a(a){}/*explicit A(int a):_a(a){}*/ private:int _a; }; int main() {//自定義類型// 當(dāng)添加explicit之后就不讓轉(zhuǎn)換了//單參數(shù)構(gòu)造函數(shù),支持隱士類型轉(zhuǎn)換A aa(1);A aa2 = 2;//隱士類型轉(zhuǎn)換int->A//同理string s1("hello");string s2="world";//const char* ->stringvector<string>v;v.push_back("world");//C++11-多參數(shù),支持隱士類型轉(zhuǎn)換Point p{ 1,2 };B b{1,2};//兼容C語言int* ptr1 = new int[5] {1, 2, 3, 4, 5};Point* ptr2 = new Point[2]{ {1,2},{3,4} };//內(nèi)置類型int x1 = { 10 };int x2{ 10 };int x3{ 1 + 2 };int x4 = { 1 + 2 };int arr1[5]{ 1,2,3,4,5 };int arr2[]{ 1,2,3,4,5 };//動(dòng)態(tài)數(shù)組C++98中不支持int* arr3 = new int[5] {1, 2, 3, 4, 5};vector<int>v{ 1,2,3,4,5 };map<int, int> m{ {1,1},{2,2},{3,3} };return 0; }多個(gè)對(duì)象的列表初始化
多個(gè)對(duì)象想要支持列表初始化,需給該類(模板類)添加一個(gè)帶有initializer_list類型參數(shù)的構(gòu)造函數(shù)即可,常見的類比如vector,list,map,set在C++11中都支持initializer_list類型參數(shù)的構(gòu)造函數(shù)。注意:initializer_list是系統(tǒng)自定義的類模板,該類模板中主要有三個(gè)方法:begin()、end()迭代器以及獲取區(qū)間中元素個(gè)數(shù)的方法size()。
編譯器自己識(shí)別{}為initializer_list類型進(jìn)行轉(zhuǎn)化。
比如vector:
#include <initializer_list> template<class T> class Vector { public:// ... Vector(initializer_list<T> l) : _capacity(l.size()), _size(0){_array = new T[_capacity];for (auto e : l)_array[_size++] = e;}Vector<T>& operator=(initializer_list<T> l) {delete[] _array;size_t i = 0;for (auto e : l)_array[i++] = e;return *this;}// ... private:T* _array;size_t _capacity;size_t _size; }; int main() {//自己實(shí)現(xiàn)的Vector<int> vv = { 1,2,3,4,5 };Vector<int> vv2 = vv;//多個(gè)對(duì)象支持列表初始化vector<int>v1 = {1,2,3,4,5};list<int> l1 = {1,2,3,4,5};pair<string, string>kv("left","左邊");map<string, string>dict = { {"insert","插入"},kv};initializer_list<int> ilt = {1,2,32,4,5}; }自己編寫list使用initializer_list支持多對(duì)象的列表初始化
注意:如果使用迭代器時(shí)報(bào)錯(cuò),前面加上typename initializer_list<T>:;iterator ,這是在類模板中再去找他的內(nèi)嵌類型,未實(shí)例化之前可能取不到。告訴編譯器類模板實(shí)例化了之后再去調(diào)用類里面的迭代器。
二 stl中的一些變化
- 新增一些容器
- 已有容器增加一些好用或者提高效率的接口。比如列表初始化initializer_list,右值引用相關(guān)接口提高效率。移動(dòng)構(gòu)造,移動(dòng)賦值。cbegin(),cend(),emplace_back(),emplace().
array
定長數(shù)組,相比于變長數(shù)組vector。
優(yōu)點(diǎn):支持迭代器,更好的兼容STL容器。對(duì)于越界的檢查。
std::array template < class T, size_t N > class array;int main() {array<int, 10>a;int a1[10];//數(shù)組a1[14] = 0;//抽查行為,*(a+14)=0,越界可能不檢查a[14] = 0;//a.operator[](14)=0,對(duì)于函數(shù)的調(diào)用,肯定檢查return 0; }forword_list
forward_list,單鏈表,支持頭插頭刪(push_front() pop_front()),支持在節(jié)點(diǎn)后面插入刪除,不支持尾插尾刪和在節(jié)點(diǎn)之前的操作
unordered_map unordered_set
三 右值引用和移動(dòng)語義
C++98中提出了引用的概念,引用即別名,引用變量與其引用實(shí)體公共同一塊內(nèi)存空間,而引用的底層是通過指針來實(shí)現(xiàn)的,因此使用引用,可以提高程序的可讀性。
左值與右值是C語言中的概念,但C標(biāo)準(zhǔn)并沒有給出嚴(yán)格的區(qū)分方式,一般認(rèn)為:
-
可以放在=左邊的,變量或者解引用的指針,我們可以獲取他的地址+可以對(duì)他賦值,稱為左值。const修飾的左值可以取地址,但是不可以賦值。
-
只能放在=右邊的,或者不能取地址的稱為右值。表示數(shù)據(jù)的表達(dá)式如:字面常量,表達(dá)式返回值,傳值返回函數(shù)的返回值,臨時(shí)對(duì)象也是右值。
左值引用& vs 右值引用&&
const int b=10;//常量,函數(shù)返回值等不能取地址的都是右值 int main() { //可以取地址對(duì)象就是左值const int b = 10;//b是左值,可以取地址不能賦值const int& r3=b; //左值引用 //右值double x = 1.1, y = 2.2;x + y;double &&r4=fmin(x,y);//右值引用就是右值的別名int&& rr1 = 10;//左值引用不能直接引用右值,得加上constconst int& r1 = x + y;const int& r2 = 10;const int& r3 = fmin(x,y);const int& p1 = (10+20);//void push_back(const T& x)這樣傳參,對(duì)面左值引用和右值引用均可傳過去//右值引用不能直接引用左值,但是可以右值引用move以后的左值int d = 10;int* p = &d;int*&& rr1 = move(p);int n = 10;int&& p2 = move(n);const int p = 20;const int&& rr2 = move(p2); //給右值取別名之后會(huì)改變存儲(chǔ)位置,另開辟個(gè)空間存儲(chǔ)右值,別名就是個(gè)左值了cout<<&p2<<endl;return 0; }右值引用的應(yīng)用
- 為了彌補(bǔ)左值引用的不足。
左值引用:傳值傳參會(huì)調(diào)用拷貝構(gòu)造函數(shù),作為參數(shù)基本完美解決問題;作為返回值以下問題就不完美只能解決部分問題,所以可用右值引用優(yōu)化。
string& operator+=(char ch) {push_back(ch);return *this;//處理作用域還在,就很完美 } string operator+(char ch) {string tmp(*this);push_back(ch);return tmp;//出了作用域就會(huì)被銷毀,傳值返回會(huì)多一次拷貝構(gòu)造,然后再析構(gòu),不完美。//所以只能用傳值返回是右值 }- 右值引用如何解決operator+的拷貝構(gòu)造問題呢?
提供一個(gè)移動(dòng)構(gòu)造,是右值只會(huì)去移動(dòng)構(gòu)造中,走最匹配的那個(gè)函數(shù)。
注意:C++11中將右值分為:自定義類型叫將亡值,或者純右值。
移動(dòng)構(gòu)造
//拷貝構(gòu)造 string(const string &s)//左值:_str(nullptr),_size(0),_capacity(0) {string tmp(s._str);swap(tmp); }//移動(dòng)構(gòu)造,一種資源轉(zhuǎn)移,避免了資源的些許浪費(fèi),少一層拷貝 string(string &&s)//右值(臨時(shí)對(duì)象也是一種右值):str(nullptr) {this->swap(s);return *this; }int main() {string s("hello world");string s1 = s;string s2 = move(s);//將s左值的屬性修改為右值屬性,賦予了別人將自己的資源拿走的權(quán)利//單純的move并不會(huì)對(duì)于s造成影響,當(dāng)傳給別人時(shí)就會(huì)有影響。return 0; } //右值引用理解場景2 string to_string(int value) {string str;while(value){int val=value%10;str+=('0'+val);value/=10;}reverse(str.begin(),str.end());return str; } int main() {string ret=to_string(1234); }? 首先,如果編譯器不優(yōu)化,str拷貝構(gòu)造臨時(shí)對(duì)象,臨時(shí)對(duì)象(在main函數(shù)的棧幀中)作為to_string的返回值構(gòu)造ret。優(yōu)化之后,在to_string結(jié)束之前,用str構(gòu)造ret,從兩次拷貝構(gòu)造優(yōu)化為只有一次拷貝構(gòu)造。
-
什么情況可優(yōu)化?
當(dāng)用臨時(shí)對(duì)象去構(gòu)造ret時(shí),也就是有ret接收時(shí)會(huì)進(jìn)行優(yōu)化。當(dāng)沒有ret時(shí),因?yàn)閟tr在to_string函數(shù)結(jié)束之后要被銷毀,必須得有一個(gè)臨時(shí)對(duì)象作為返回值返回,還沒人接收時(shí)就無法優(yōu)化。
? 如果有了移動(dòng)構(gòu)造之后,
-
如果不考慮優(yōu)化的存在,原來的兩次拷貝構(gòu)造變成一次拷貝構(gòu)造,一次移動(dòng)構(gòu)造,因?yàn)閟tr是左值調(diào)用一次拷貝構(gòu)造產(chǎn)生臨時(shí)變量,此時(shí)產(chǎn)生的拷貝的臨時(shí)對(duì)象被認(rèn)為是右值,調(diào)用移動(dòng)構(gòu)造。
-
優(yōu)化之后,就變成了一次移動(dòng)構(gòu)造,魯莽地直接將str認(rèn)為是右值,
-
如果有ret接收,直接移動(dòng)構(gòu)造交給ret,完成一次資源轉(zhuǎn)移。
-
如果沒有ret接收,之前直接將ret視為右值的存在在to_string()函數(shù)銷毀時(shí)作為函數(shù)返回值返回即可。
-
-
資源轉(zhuǎn)移:有ret接收,就資源轉(zhuǎn)移給ret,如果沒ret接收,就資源轉(zhuǎn)移給一個(gè)函數(shù)必有的函數(shù)返回值。
- 移動(dòng)構(gòu)造提升效率一個(gè)例子:
使得C++11 效率更高,當(dāng)然,如果vv是靜態(tài)或者是全局的,出了作用域還在,直接用左值返回就OK了。
移動(dòng)賦值
//移動(dòng)賦值 string& operator=(string&& s) {cout<<"轉(zhuǎn)移資源"<<endl;swap(s);return *this; } //正常深拷貝賦值 string & operator=(const string &s) {cout<<"深拷貝"<<endl;string tmp(s);swap(tmp);return *this; } int main() {yuanwei::string s1("hello");yuanwei::string s2("world");s2=move(s1);//此時(shí)是右值,走匹配的右值引用,走移動(dòng)賦值函數(shù)實(shí)現(xiàn)資源轉(zhuǎn)移//避免移動(dòng)構(gòu)造,先有一個(gè)對(duì)象string ret;ret=to_string(12345); }如果有移動(dòng)賦值時(shí),將str作為右值直接進(jìn)行移動(dòng)構(gòu)造,資源轉(zhuǎn)移給臨時(shí)對(duì)象,再用臨時(shí)對(duì)象移動(dòng)賦值給ret,兩次資源轉(zhuǎn)移。將臨時(shí)對(duì)象這個(gè)已經(jīng)存在的對(duì)象交給ret這個(gè)已經(jīng)存在的對(duì)象。
如果沒有移動(dòng)賦值函數(shù),to_string()的str被識(shí)別為右值移動(dòng)構(gòu)造資源轉(zhuǎn)移給臨時(shí)對(duì)象,臨時(shí)對(duì)象賦值走一遍深拷貝交給已經(jīng)存在的ret。
- 容器的插入接口也提供了一個(gè)右值引用的版本
完美轉(zhuǎn)發(fā)
模板中的&&不代表右值引用,而是萬能引用,其既能接收左值又能接收右值。
模板的萬能引用只是提供了能夠接收同時(shí)接收左值引用和右值引用的能力,
但是引用類型的唯一作用就是限制了接收的類型,后續(xù)使用中都退化成了左值,
我們希望能夠在傳遞過程中保持它的左值或者右值的屬性, 就需要用我們下面學(xué)習(xí)的完美轉(zhuǎn)發(fā)
- 退化的例子,如圖所示:
使用完美轉(zhuǎn)發(fā)之后:
- 只要右值引用,再傳遞其他函數(shù)調(diào)用,要保持右值屬性,必須用完美轉(zhuǎn)發(fā),然后走的yuanwei::string的移動(dòng)賦值,資源轉(zhuǎn)移。
emplace_back()
push_back VS emplace_back
//二者相比,右值版本不會(huì)更加高效,差不多 //左值版本 emplace 會(huì)更高效,因?yàn)樗淮嬖谏羁截惖膯栴} int main() {std::list<pair<int, char>>mylist;//兩次資源轉(zhuǎn)移,先構(gòu)造右值,再移動(dòng)構(gòu)造mylist.push_back(make_pair(1,'A'));//一個(gè)類型參數(shù)mylist.push_back({1,'a'});//pair支持{}初始化//兩次直接構(gòu)造mylist.emplace_back(make_pair(1,'a'));//整體作為單參數(shù)對(duì)象mylist.emplace_back(1,'a');//多參數(shù)傳值也支持return 0; }emplace_back()是直接構(gòu)造,pushback()是先構(gòu)造對(duì)象,然后再進(jìn)行資源轉(zhuǎn)移.
類的新功能
-
原來的默認(rèn)構(gòu)造函數(shù)有6個(gè),重要的有4個(gè):析構(gòu)函數(shù),構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值函數(shù).
- 默認(rèn)生成的構(gòu)造函數(shù)不寫的話,會(huì)自動(dòng)生成并在初始化列表階段調(diào)用自定義成員的構(gòu)造函數(shù).
-
新的是移動(dòng)構(gòu)造函數(shù),移動(dòng)賦值函數(shù)。這倆的使用規(guī)則是相同的,下面只介紹一個(gè):
如果在類中,你沒有實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù),并且你沒有實(shí)現(xiàn)你的析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值函數(shù)其中一個(gè),那么會(huì)給你生成默認(rèn)移動(dòng)構(gòu)造函數(shù);他對(duì)于內(nèi)置類型是采用值拷貝的方式,對(duì)于自定義類型,當(dāng)自定義類型本身是提供移動(dòng)構(gòu)造函數(shù)的話,就調(diào)用。如果沒有,就去調(diào)用拷貝構(gòu)造函數(shù)。
同理移動(dòng)賦值函數(shù)。
類成員初始化
類內(nèi)聲明內(nèi)置類型的時(shí)候給個(gè)缺省值
四 可調(diào)用對(duì)象類型
- C-函數(shù)指針void(*p)();
- C++98-仿函數(shù)/函數(shù)對(duì)象
- C++11-lambda表達(dá)式/匿名函數(shù)
lambda表達(dá)式
lambda表達(dá)式書寫格式:[capture-list] (parameters) mutable -> return-type { statement }
lambda表達(dá)式各部分說明
[capture-list] : 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開始位置,編譯器根據(jù)[]來判斷接下來的代碼是否為lambda函數(shù)**,**捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用。所以不能省略。
(parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略
mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。
->returntype:返回值類型。用追蹤返回類型形式聲明函數(shù)的返回值類型,沒有返回值時(shí)此部分可省略。返回值類型明確情況下,也可省略,由編譯器對(duì)返回類型進(jìn)行推導(dǎo)。
{statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
注意: 在lambda函數(shù)定義中,參數(shù)列表和返回值類型都是可選部分,而捕捉列表和函數(shù)體可以為空。因此C++11中最簡單的lambda函數(shù)為:[]{}; 該lambda函數(shù)不能做任何事情。
int main() {//lambda實(shí)現(xiàn)兩個(gè)數(shù)相加的功能auto add1=[](int a,int b) ->int {return a + b; };//調(diào)用方式cout<<add1(1,2)<<endl;auto add3=add1;decltype(add1) add3=add1; //返回值可以省略auto add2 = [](int a,int b){return a + b; }; //沒有參數(shù)可以省略參數(shù)列表沒有返回值auto func1 = [] { cout << "hello" << endl; }; //捕捉列表不能省略[]auto swap1 = [](int& a,int& b){int c = a;a = b;b = c; };swap1(3,4);int a = 3, b = 4; auto fun1 = [&](int c) {b = a + c; };//auto fun1 = [&a,&b](int c) {b = a + c; };//傳引用捕捉才能改變值fun1(10);cout << a << " " << b << endl;int a = 0;int b = 1; //傳值捕捉,調(diào)用之后并不能改變ab的值,只是一份拷貝交換了.//加上mutable lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符,參數(shù)列表不能省略。//但是還是不能交換成功auto swap2 = [a, b]()mutable {int c = a;a = b;b = c;};swap2();//捕捉就不需要傳參數(shù)了//傳引用捕捉auto swap3 = [&a, &b]() {int c = a; a = b;b = c;};swap3(); }捕捉聲明:
捕捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
[var]:表示值傳遞方式捕捉變量var
[=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this),如果指定某一個(gè)變量進(jìn)行值傳遞,[a,b]指明出來
[&var]:表示引用傳遞指定的捕捉變量var
[&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
[this]:表示值傳遞方式捕捉當(dāng)前的this指針
注意事項(xiàng):
a. 父作用域指包含lambda函數(shù)的語句塊
b. 語法上捕捉列表可由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分割。比如:[=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量 [&,a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量
c. 捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯錯(cuò)誤。 比如:[=, a]:=已經(jīng)以值傳遞方式捕捉了所有變量,捕捉a重復(fù)。
d. 在塊作用域以外的lambda函數(shù)捕捉列表必須為空。在全局中無法捕捉變量。
e. 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會(huì)導(dǎo)致編譯報(bào)錯(cuò)。
f. lambda表達(dá)式之間不能相互賦值,即使看起來類型相同
void (*PF)(); int main() {auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };//f1 = f2; // 編譯失敗--->提示找不到operator=()// 允許使用一個(gè)lambda表達(dá)式拷貝構(gòu)造一個(gè)新的副本auto f3(f2);f3();// 可以將lambda表達(dá)式賦值給相同類型的函數(shù)指針PF = f2;PF();return 0; }函數(shù)對(duì)象(仿函數(shù))與lambda表達(dá)式
函數(shù)對(duì)象,又稱為仿函數(shù),即可以像函數(shù)一樣使用的對(duì)象,就是在類中重載了operator()運(yùn)算符的類對(duì)象。
lambda表達(dá)式,底層原理其實(shí)是被處理成一個(gè)lambda_uuid的一個(gè)仿函數(shù)類。
class Rate { public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;} private:double _rate; }; int main() {// 函數(shù)對(duì)象double rate = 0.49;Rate r1(rate);r1(10000, 2);// lamberauto r2 = [=](double monty, int year)->double {return monty * rate * year; };r2(10000, 2);//cout << typeid(r2).name() <<endl;return 0; }從使用方式上來看,函數(shù)對(duì)象與lambda表達(dá)式完全一樣。函數(shù)對(duì)象將rate作為其成員變量,在定義對(duì)象時(shí)給出初始值即可,lambda表達(dá)式通過捕獲列表可以直接將該變量捕獲到。
實(shí)際在底層編譯器對(duì)于lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,即:如果定義了一個(gè)lambda表達(dá)式,編譯器會(huì)**自動(dòng)生成一個(gè)類,在該類中重載了operator()。**仿函數(shù)對(duì)象去調(diào)用operator()。
五 關(guān)鍵字
auto
在定義變量時(shí),必須先給出變量的實(shí)際類型,編譯器才允許定義,但有些情況下可能不知道需要實(shí)際類型怎
么給,或者類型寫起來特別復(fù)雜。C++11中,可以使用auto來根據(jù)變量初始化表達(dá)式類型推導(dǎo)變量的實(shí)際類型,可以給程序的書寫提供許多方便。將程序中c與it的類型換成auto,程序可以通過編譯,而且更加簡潔
decltype
auto使用的前提是:必須要對(duì)auto聲明的類型進(jìn)行初始化,否則編譯器無法推導(dǎo)出auto的實(shí)際類型。但有時(shí)候可能需要根據(jù)表達(dá)式運(yùn)行完成之后結(jié)果的類型進(jìn)行推導(dǎo),因?yàn)榫幾g期間,代碼不會(huì)運(yùn)行,此時(shí)auto也就無能為力。如果能用加完之后結(jié)果的實(shí)際類型作為函數(shù)的返回值類型就不會(huì)出錯(cuò),但這需要程序運(yùn)行完才能知道結(jié)果的實(shí)際類型,即RTTI(Run-Time Type Identifification 運(yùn)行時(shí)類型識(shí)別)。
C++98中確實(shí)已經(jīng)支持RTTI:typeid只能查看類型不能用其結(jié)果類定義類型。dynamic_cast只能應(yīng)用于含有虛函數(shù)的繼承體系中運(yùn)行時(shí)類型識(shí)別的缺陷是降低程序運(yùn)行的效率。
int func(int a) {return a; } int main() {int a = 10;int b = 20;// 用decltype推演a+b的實(shí)際類型,作為定義c的類型decltype(a + b) c;//C++98 const int ->int 會(huì)存在區(qū)別,decltype就不會(huì)存在cout << typeid(c).name() << endl;//聲明函數(shù)指針類型int(*pfunc1)(int) = func;auto pfunc2 = func;decltype(pfunc2) pfunc3 = func;//和auto配合使用decltype(&func) pfunc4 = func;map<string, string> dict = { {"left","左邊"}};auto it = dict.begin();//decltype的使用場景:要頂一個(gè)auto推導(dǎo)對(duì)象的拷貝decltype(it) copyIt1 = it;auto copyIt2 = it;//vector<auto>無法通過編譯vector<decltype(it)> v;v.push_back(it); }final-不讓繼承
override-檢查能否被重寫
default
? 在C++中對(duì)于空類編譯器會(huì)生成一些默認(rèn)的成員函數(shù),比如:構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、運(yùn)算符重載、析構(gòu)函數(shù)和&和const&的重載、移動(dòng)構(gòu)造、移動(dòng)拷貝構(gòu)造等函數(shù)。如果在類中顯式定義了,編譯器將不會(huì)重新生成默認(rèn)版本。有時(shí)候這樣的規(guī)則可能被忘記,最常見的是聲明了帶參數(shù)的構(gòu)造函數(shù),必要時(shí)則需要定義不帶參數(shù)的版本以實(shí)例化無參的對(duì)象。而且有時(shí)編譯器會(huì)生成,有時(shí)又不生成,容易造成混亂,于是C++11讓程序員可以控制是否需要編譯器生成。顯示缺省函數(shù)。在C++11中,可以在默認(rèn)函數(shù)定義或者聲明時(shí)加上=default,從而顯式的指示編譯器生成該函數(shù)的默認(rèn)版本,用=default修飾的函數(shù)稱為顯式缺省函數(shù)。
class A { public:A(int a): _a(a){}// 顯式缺省構(gòu)造函數(shù),由編譯器生成A() = default;// 在類中聲明,在類外定義時(shí)讓編譯器生成默認(rèn)賦值運(yùn)算符重載A& operator=(const A& a); private:int _a; }; A& A::operator=(const A& a) = default; int main() {A a1(10);A a2;a2 = a1;return 0; }delete
刪除默認(rèn)函數(shù)。如果能想要限制某些默認(rèn)函數(shù)的生成,在C++98中,是該函數(shù)設(shè)置成private,并且不給定義,這樣只要其他人想要調(diào)用就會(huì)報(bào)錯(cuò)。在C++11中更簡單,只需在該函數(shù)聲明加上=delete即可,該語法指示編譯器不生成對(duì)應(yīng)函數(shù)的默認(rèn)版本,稱=delete修飾的函數(shù)為刪除函數(shù).
比如:單例模式下,規(guī)定聲明的對(duì)象只能有一個(gè),這個(gè)對(duì)象是不允許被拷貝構(gòu)造其他的對(duì)象的,C++98 的方法一是,讓拷貝構(gòu)造函數(shù)私有化,在類的外面就調(diào)用不到了,但是呢,在類的里面中的某個(gè)函數(shù)還是可以調(diào)用的,同時(shí)造成默認(rèn)構(gòu)造函數(shù)就無法生成了。如果類的里面也不讓調(diào)用時(shí),也就是方法二:在私有中,只聲明但是不實(shí)現(xiàn)來防止拷貝。注意:如果不設(shè)置成私有的話,別人可以在類的外面實(shí)現(xiàn)拷貝構(gòu)造函數(shù),從而控制你提供的類做出修改。
C++11 就直接提供了關(guān)鍵字delete,在拷貝構(gòu)造函數(shù)的后面加上:delete 函數(shù)就變成了已刪除函數(shù),也無論私有與否了。
避免刪除函數(shù)和explicit一起使(explicit是防止函數(shù)進(jìn)行隱式類型轉(zhuǎn)換)
class A {public:A(int a): _a(a){}// 禁止編譯器生成默認(rèn)的拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載A(const A&) = delete;A& operator(const A&) = delete;private:int _a; }; int main() {A a1(10);// 編譯失敗,因?yàn)樵擃悰]有拷貝構(gòu)造函數(shù)//A a2(a1);// 編譯失敗,因?yàn)樵擃悰]有賦值運(yùn)算符重載A a3(20);a3 = a2;return 0; }六 模板的可變參數(shù)
- printf
- 解析打印參數(shù)包中的類型和值
逗號(hào)表達(dá)式展開參數(shù)包
//參數(shù)包,傳一個(gè)或者多個(gè) template<class T> void PrintArg(T val) {T copy(val);cout << typeid(T).name() << ":" << val << endl; } template<class ...Args> void ShowList(Args... args) {//{}列表初始化,開多大空間取決于可變參數(shù)個(gè)數(shù),依次取出參數(shù)包//但是函數(shù)沒有返回值,創(chuàng)建數(shù)組需要元素有返回值,所以用逗號(hào)表達(dá)式,帶個(gè)0int arr[] = {(PrintArg(args),0)...};//逗號(hào)表達(dá)式,0是返回值。cout << endl; } //帶返回值,不用逗號(hào)表達(dá)式 template<class T> int PrintArg(T val) {T copy(val);cout << typeid(T).name() << ":" << val << endl;return 0; } template<class ...Args> void ShowList(Args... args) {int arr[] = { PrintArg(args)... };//0是返回值cout << endl; } int main() {ShowList(1);ShowList(1, 'A');ShowList(1,'A',string("sort"));return 0; } endl;ShowList(args...);//遞歸依次到達(dá)下一個(gè)參數(shù) } int main() {ShowList(1);ShowList(1, 'A');ShowList(1,'A',string("sort"));return 0; }總結(jié)
- 上一篇: 管培生走下神坛,“高管捷径”破灭
- 下一篇: C++【C++11】