【C++】C++11 篇二
- 前言
- 移動構造函數
- 移動賦值運算符重載
- 類成員變量初始化 (缺省值出自C++11
- 強制生成默認函數的關鍵字default:
- 禁止生成默認函數的關鍵字delete:
- 繼承和多態中的final與override關鍵字(出自C++11
- 可變參數模板
- 遞歸函數方式展開參數包
- 逗號表達式展開參數包
- STL容器中的empalce相關接口函數
- lambda表達式
- 實例
- lambda表達式語法
- 實例:
- lambda表達式書寫格式:[capture-list] (parameters) mutable -> return-type { statement }
- 捕獲列表說明
- 函數對象(仿函數)與lambda表達式
前言
C++98的6個默認成員函數:
- 構造函數
- 析構函數
- 拷貝構造函數
- 拷貝賦值重載
- 取地址重載 //用處不大
- const 取地址重載 //用處不大
C++111 新增了兩個:移動構造函數和移動賦值運算符重載
移動構造函數
如果沒有自己實現移動構造函數,且析構函數 、拷貝構造、拷貝賦值重載都沒有實現。
那么編譯器會自動生成一個默認移動構造。
默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝,
自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造
移動賦值運算符重載
(默認移動賦值跟上面移動構造完全類似,把移動構造函數替換為移動賦值運算符重載即可)
類成員變量初始化 (缺省值出自C++11
C++11允許在類定義時給成員變量初始缺省值,默認生成構造函數會使用這些缺省值初始化
強制生成默認函數的關鍵字default:
提供了拷貝構造,就不會生成移動構造了,使用default關鍵字強制生成
Person(Person&& p) = default;
禁止生成默認函數的關鍵字delete:
Person(Person&& p) = delete;
繼承和多態中的final與override關鍵字(出自C++11
可變參數模板
// Args是一個模板參數包,args是一個函數形參參數包
// 聲明一個參數包Args…args,這個參數包中可以包含0到任意個模板參數
//C語言的printf scanf函數的參數就是可變的
//語法不支持使用args[i]這樣方式獲取可變參數
template <class ...Args>
void ShowList(Args... args)
{}
遞歸函數方式展開參數包
// 遞歸終止函數
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}
// 展開函數
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value <<" ";ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}
逗號表達式展開參數包
//由于是逗號表達式,在創建數組的過程中會先執行逗號表達式前面的部分printarg(args)
打印出參數,再得到逗號表達式的結果0
//通過初始化列表來初始化一個變長數組, {(printarg(args), 0)…}將會展開成((printarg(arg1),0),
(printarg(arg2),0), (printarg(arg3),0), etc… ),最終會創建一個元素值都為0的數組int arr[sizeof…(Args)]。
template <class T>
void PrintArg(T t)
{cout << t << " ";
}
//展開函數
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}
STL容器中的empalce相關接口函數
template <class… Args>
void emplace_back (Args&&… args);
emplace系列的接口,支持模板的可變參數,并且萬能引用。那么相對insert和
emplace系列接口的優勢到底在哪里呢?
int main()
{std::list< std::pair<int, char> > mylist;// emplace_back支持可變參數,拿到構建pair對象的參數后自己去創建對象// 那么在這里我們可以看到除了用法上,和push_back沒什么太大的區別mylist.emplace_back(10, 'a');//避免了臨時對象的創建和拷貝 / 移動,效率更高mylist.emplace_back(make_pair(30, 'c'));//相當于退化為了 push_back() 的效果。//:push_back() 必須先有一個完整的對象(可能是臨時對象),再將其放入容器,比直接構造多一次拷貝 / 移動操作(在優化前)。mylist.push_back(make_pair(40, 'd'));mylist.push_back({ 50, 'e' });//只是用初始化列表簡化了對象的創建,仍會產生臨時對象(調用構造函數)for (auto e : mylist)cout << e.first << ":" << e.second << endl;return 0;
}
lambda表達式
實例
#include <algorithm>
#include <functional>
int main()
{
int array[] = {4,1,8,5,3,7,0,9,2,6};
// 默認升序
std::sort(array, array+sizeof(array)/sizeof(array[0]));
// 降序greater<int>()依賴#include <functional>
std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
return 0;
}
待排序元素為自定義類型,需要用戶定義排序時的比較規則,仿函數
struct Goods
{string _name; // 名字double _price; // 價格int _evaluate; // 評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
struct ComparePriceLess
{bool operator()(const Goods& gl, const Goods& gr){return gl._price < gr._price;}
};
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};
int main()
{vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}
lambda表達式(匿名函數),其實底層就是仿函數
int main()
{vector<Goods> v = { { "蘋果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price < g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price > g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate < g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate > g2._evaluate; });
}
lambda表達式語法
實例:
int main()
{// 最簡單的lambda表達式, 該lambda表達式沒有任何意義[]{}; // 省略參數列表和返回值類型,返回值類型由編譯器推導為intint a = 3, b = 4;[=]{return a + 3; }; // 省略了返回值類型,無返回值類型auto fun1 = [&](int c){b = a + c; }; fun1(10)cout<<a<<" "<<b<<endl;// 各部分都很完善的lambda函數auto fun2 = [=, &b](int c)->int{return b += a+ c; }; cout<<fun2(10)<<endl;// 復制捕捉xint x = 10;auto add_x = [x](int a) mutable { x *= 2; return a + x; }; cout << add_x(10) << endl; return 0;
}
lambda表達式實際上可以理解為無名函數,該函數無法直接調用,如果想要直接調用,可借助auto將其賦值給一個變量
lambda表達式書寫格式:[capture-list] (parameters) mutable -> return-type { statement }
- [capture-list] : 捕捉列表,編譯器根據[]來判斷接下來的代碼是否為lambda函數,捕捉上下文中的變量供lambda
函數使用。 - (parameters):參數列表。與普通函數的參數列表一致,如果不需要參數傳遞,則可以
連同()一起省略 - mutable:默認情況下,lambda函數總是一個const函數,mutable可以取消其常量性。使用該修飾符時,參數列表不可省略(即使參數為空)。
- returntype:返回值類型。可省略,由編譯器對返回類型進行推導。
- {statement}:函數體。在該函數體內,可以使用其參數外,還可使用捕獲
到的變量。
注意: 在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為
空。因此C++11中最簡單的lambda函數為:[]{}; 該lambda函數不能做任何事情
捕獲列表說明
捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。
[var]:表示值傳遞方式捕捉變量var
[=]:表示值傳遞方式捕獲所有父作用域中的變量**(包括this)**
[&var]:表示引用傳遞捕捉變量var
[&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
[this]:表示值傳遞方式捕捉當前的this指針
注意:
a. 父作用域指包含lambda函數的語句塊
b. 語法上捕捉列表可由多個捕捉項組成,并以逗號分割。
比如:[=,&a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量
[&,a,this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量
c. 捕捉列表不允許變量重復傳遞,否則就會導致編譯錯誤。
比如:[=, a]:=已經以值傳遞方式捕捉了所有變量,捕捉a重復
d. 在塊作用域以外的lambda函數捕捉列表必須為空。
e. 在塊作用域中的lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會導致編譯報錯。
f. lambda表達式之間不能相互賦值,類型不同,其類型構成含有uuid,通用唯一標識符
void (*PF)();
int main()
{auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };//f1 = f2; //類型不同,不可賦值// 允許使用一個lambda表達式拷貝構造一個新的副本auto f3(f2);//默認包含 operator() 重載(用于調用 Lambda 邏輯)f3();// 可以將lambda表達式賦值給相同類型的函數指針PF = f2;PF();return 0;
}
小結
- Lambda 表達式會被編譯器隱式轉換為一個匿名的函數對象類型
- 是編譯器自動生成的、唯一的類類型
- 默認包含 operator() 重載(用于調用 Lambda 邏輯)
- 對于沒有捕獲變量的 Lambda,可隱式轉換為函數指針;有捕獲變量的 Lambda 則不能
函數對象(仿函數)與lambda表達式
函數對象,又稱為仿函數,即可以像函數一樣使用的對象,就是在類中重載了operator()運算符 的 類對象
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}
private:double _rate;
};
int main()
{// 函數對象double rate = 0.49;Rate r1(rate);//僅僅調構造cout << r1(10000, 2);//僅僅調operator()cout << endl;// lamber//r1 雖然是外部變量,但未在 Lambda 表達式中使用,因此不會被捕捉auto r2 = [=](double monty, int year)->double {return monty * rate * year; };cout << r2(10000, 2);return 0;
}
函數對象將rate作為其成員變量,在定義對象時給出初始值即可,lambda表達式通過捕獲列表可
以直接將該變量捕獲到。
在底層編譯器對于lambda表達式的處理方式,完全就是按照函數對象的方式處理的,
即:如果定義了一個lambda表達式,編譯器會自動生成一個類,在該類中重載了operator()。