C++11
1.列表初始化:
//允許以下代碼正確運行int a[]{1,2,3};//效果與int a[]={1,2,3}一致
即允許省略等于號。同時,允許用花括號對所有自定義類型和內置類型進行初始化,而非以前花括號只能對數組進行初始化。利用花括號對自定義類型初始化時,相當于調用了其構造函數。
2.std::initializer_list
一般用作構造函數的參數,接受一系列的初始值,并以此賦給每個對象。
因此,允許以下操作:
map<int,string>v={{1,"one"},{2,"two"}};
//其中{1,"one"}和{2,"two"}均對應一個pair<int,string>的對象,用于給map賦值。
3.decltype
可以獲得變量的類型,并且可以用這個類型構造變量(與typeid不同,typeid只能得到類型,不能用來定義變量)
4.右值引用
左值:可以取地址的表達式,因為可以取地址因此可以給其賦值,所以可以出現"="的左右任意一側。對左值進行引用,就是給其取個別名,稱為左值引用。
右值:不可以取地址的表達式,無法給其賦值,因此只能出現在"="右側,對右值進行引用,稱為右值引用。常見的右值有表達式返回值、常量、函數返回值(返回非左值引用的類型才行)。
需注意,右值不能取地址,無法給其賦值,但是,右值引用后會有一份地址保存這個值,就可以通過這個地址對其進行修改。若不希望被修改,就用const修飾右值引用。
左值引用與右值引用區別:
左值引用:正常只能引用左值,用const修飾可以引用左值和右值
右值引用:正常只能引用右值,但是給左值加move可以被右值引用
int i=1;
int &&a=move(i); //此處i是左值,但move(i)返回的是右值
右值引用應用:
常用于函數返回臨時對象時,此時無法返回左值引用,因為變量是出了函數作用域就銷毀的。若不用右值引用,則正常需要進行兩次深拷貝(編譯器可能優化成一次),但右值引用可以不進行深拷貝,極大提高了效率。
而右值引用的原理是,認為臨時對象出了作用域就要被銷毀,因此可以直接拿過來臨時對象的成員,這樣就不用進行深拷貝了。這就是移動構造的思想。
同時,也有移動賦值,函數參數是右值引用,原理也是直接交換對象的成員,而非深拷貝。
5.完美轉發:
用于有萬能模版的情況下。而萬能模版就是一種特殊的模版函數,其成員是T&&val,此時可以接受左值引用也可以接受右值引用,因此是萬能模版。而無論是左值引用還是右值引用,其類型都是左值(注:是右值引用是左值,不是右值是左值!),因此,若用右值引用做參數,只能調用以左值引用為形參的函數,無法調用以右值為形參的函數,若想要調用只能用move,但這樣就只能調用右值引用為參數的。為了實現左值引用可以調用左值為參數的函數,右值引用可以調用右值為參數的函數,在引用前加一個forward,效果是右值引用則返回一個右值,左值引用就返回一個左值。
6.默認成員函數的增加:
增加了默認移動構造和默認移動賦值構造,編譯器自己寫默認移動構造函數的前提是:
1.用戶自己未編寫移動構造
2.用戶為編寫拷貝構造、賦值構造和析構函數。
使用:對于內置類型進行逐字節拷貝,對于自定義類型若有移動構造調用移動構造,若沒有調用拷貝構造。
(對于默認移動賦值前提和使用極其類型,就是把移動構造改成移動賦值即可)
7.強制生成/銷毀默認成員函數:
強制生成:default;? ? 強制銷毀:delete? ? 一般稱=delete的函數是刪除函數
class bb
{public:bb()=default;//強制生成bb(const bb&b)=delete;//強制刪除}
8.可變參數模版:
寫法:
template<class...Args>
返回類型 函數名(Args...args)
{?
//函數體
}
args前面有...,是可變模版參數。稱前面有...的參數為參數包,參數包里包含0到N個可變模版參數。
如何獲取參數包里的可變模版參數:
1.用遞歸展開:
void _get()
{cout<< endl;
}template<class T,class...Args>
void _get(T& val,Args...args)
{cout << val << " ";_get(args...);
}template<class...Args>
void get(Args...args)
{_get(args...);
}int main()
{get(1, 2.2, "xxx");
}
原理就是不斷用T獲取最左的那個可變模版參數,然后不斷縮小參數包內的可變模版參數的個數,最后當可變模版參數為0時調用打印空格,實現獲取所有的參數。
2.利用逗號表達式:
template<class T>
void print(T& val)
{cout<< val << " ";
}template<class...Args>
void get(Args...args)
{//_get(args...);int a[] = { (print(args),0)... };cout << endl;
}
原理是利用初始化列表展開成(print(args1),0)、(print(args2),0)...,同時利用逗號表達式依次去執行每個()內的兩條表達式,實現獲取每個參數。
9.lambda表達式
形式:
[]+()+mutable+->+{};
其中:
[]:捕捉列表,包含在于lamabda相同作用域內的變量.[var]表示捕獲變量var。[=]表示捕獲父作用域內所有變量。[&var]表示捕獲var變量的引用。[&=]表示父作用域所有變量的引用。[this]表示捕獲當前的this指針。[]內可以包含多鐘類型的捕捉,比如[&var1,var2,this],但注意相同變量不能重復捕獲。
():函數參數列表,若無參數可以省略
mutable:若無mutable則表示該lambda函數是const修飾的函數,若有則可以取消常量性。注:若加上mutable,則參數列表無論有無參數都得寫"()"。
->:指向返回類型,若返回類型是空可以省略,若返回類型十分明確也可以省略讓編譯器自動推導。
{}:函數體部分。
ps:只有[]和{}部分一定不能省略
lambda的底層代碼是仿函數,若想要用一個變量接受該表達式,則需要用auto修飾變量,因為lambda返回的類型由編譯器自己決定,無法顯示知道并調用。
10.包裝器:
頭文件是<functional>
function<返回類型(函數參數)>對象={}。
作用:可以讓對象賦值為仿函數、函數形參、lambda函數(但返回類型和函數參數必須相同)
(注:當接受的是lambda函數時,不管[],只要()和返回類型一致就行)
11.bind:
類型一個函數模版。功能是接受一個可調用對象,返回一個新的可調用對象去適應原本的函數參數,如修改函數參數的先后順序和個數。
使用:
auto newcallable=bind(callable,arg_list),其中newcallable是新生成的可調用對象,callable是原可調用對象,arg_list是一個逗號表達式,包含callable的參數。
arg_list:可以是placeholder::_x,表示當前位置對應函數第x個參數。
如上圖,newf中1對應的是placeholder::_2,故對應lambda的第二個函數參數b。
arg_list也可以是一個固定變量或常量,表示該位置是一個固定的函數參數,一般用于某個調用的參數固定不變的情況。
如上圖,第一個參數a對應的位置為常量111,此時調用時就不需要在顯示寫該參數了,因此newf調用時只寫了一個參數。