【C++】-- C++11基础常用知识点(下)
上篇:?【C++】-- C++11基礎常用知識點(上)_川入的博客-CSDN博客
目錄
新的類功能
默認成員函數
可變參數模板
可變參數
可變參數模板
empalce
lambda表達式
C++98中的一個例子
lambda表達式
lambda表達式語法
捕獲列表
lambda表達底層
包裝器
function包裝器
bind綁定
新的類功能
默認成員函數
原來C++類中,有6個默認成員函數:????????最后重要的是前4個,后兩個用處不大。默認成員函數就是我們不寫編譯器會生成一個默認的。 C++11 新增了兩個:移動構造函數和移動賦值運算符重載。
? ? ? ? 所以到了C++11后有8個默認成員函數。
移動構造函數和移動賦值運算符重載的又來以及原理:
【C++】-- C++11 - 右值引用和移動語義(上萬字詳細配圖配代碼從執行一步步講解)_川入的博客-CSDN博客
只有在深拷貝的情況下才會有移動構造函數和移動賦值運算符重載。可以認為:
- 拷貝構造函數與拷貝賦值重載:針對于左值的拷貝。
- 移動構造函數和移動賦值重載:針對于右值的拷貝。
????????移動構造函數和移動賦值重載,編譯器自行生成的默認成員函數,能用的條件的復雜度與苛刻程度遠遠大于:構造函數、析構函數 、拷貝構造函數 、拷貝賦值重載4個默認成員函數。(由于:取地址重載 、const 取地址重載幾乎不用自己寫,用編譯器的即可,所以忽略)
針對移動構造函數和移動賦值運算符重載有一些需要注意的點如下:- 編譯器生成默認移動構造函數的條件:
????????沒有自己實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個。那么編譯器會自動生成一個默認移動構造。
- 編譯器生成默認移動構造函數的實現:
????????默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝。自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造。
- 編譯器生成默認移動賦值重載函數的條件:
- 編譯器生成默認移動賦值重載函數的實現:
- 如果你提供了移動構造或者移動賦值,編譯器不會自動提供拷貝構造和拷貝賦值。
? ????????C++11可以讓我們更好的控制要使用的默認函數。假設你要使用某個默認的函數,但是因為一些原因這個函數沒有默認生成。比如:我們提供了拷貝構造,就不會生成移動構造了,那么我們可以使用default關鍵字顯示指定移動構造生成。 class Person { public:Person(const char *name = "", int age = 0): _name(name), _age(age){}Person(const Person &p): _name(p._name), _age(p._age){}Person(Person &&p) = default;private:bit::string _name;int _age; };int main() {Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0; } 禁止生成默認函數的關鍵字delete: ????????如果能想要限制某些默認函數的生成,在C++98中,是該函數設置成private,并且只聲明補丁已,這樣只要其他人想要調用就會報錯。在C++11中更簡單,只需在該函數聲明加上=delete即可,該語法指示編譯器不生成對應函數的默認版本,稱=delete修飾的函數為刪除函數。 class Person { public:Person(const char *name = "", int age = 0): _name(name), _age(age){}Person(const Person &p) = delete;private:bit::string _name;int _age; }; int main() {Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0; }
????????可以使用default關鍵字強行讓編譯器生成,但是需要注意析構函數 、拷貝構造、拷貝賦值重載也會收到影響,需要自己寫或也強制生成。沒有什么意義,所以一般default關鍵字是用于構造,因為拷貝構造也屬于構造,如果寫了拷貝構造就不會默認生成構造了。
#問:如何用delete關鍵字實現一個類,只能再堆上創建對象?
????????平時我們創建的類,是可以在棧區、全局數據區上創建的。
class HeapOnly {};int main() {HeapOnly hp1; // 棧區static HeapOnly h2; // 全局數據區return 0; }? ? ? ? 我們可以通過delete析構函數,然后使用new開辟類。
class HeapOnly { public:// HeapOnly()// {// str_ = new char[10];// }// void Destroy()// {// delete[] str_;// operator delete(this); // 內存管理之重載operator delete// }~HeapOnly() = delete; private:char* str_; };int main() {// HeapOnly hp1; // 棧區 -- 會調析構// static HeapOnly h2; // 全局數據區 -- 會調析構// new出來的對象會調用構造 -- 這個時候會導致資源泄漏HeapOnly *ptr = new HeapOnly;operator delete(ptr);return 0; }- new是c++中的操作符,malloc是c中的一個函數。
- new不止是分配內存,而且會調用類的構造函數,同理delete會調用類的析構函數
- malloc只會單純的分配內存,不會進行初始化類成員的工作,同樣free也不會調用析構函數。
#問:
class HeapOnly { public:HeapOnly(){str_ = new char[10];}~HeapOnly() = delete; private:char* str_; };????????對于構造函數是new空間,因為不能調用析構而不能使用delete,導致值空間泄漏怎么辦?
????????我們可以搞一個函數,利用函數將其釋放。
class HeapOnly { public:HeapOnly(){str_ = new char[10];}void Destroy(){delete[] str_;operator delete(this); // 內存管理之重載operator delete// 也可以使用free}~HeapOnly() = delete; private:char* str_; };int main() {// HeapOnly hp1; // 棧區 -- 會調析構// static HeapOnly h2; // 全局數據區 -- 會調析構// new出來的對象會調用構造 -- 這個時候會導致資源泄漏HeapOnly *ptr = new HeapOnly;ptr->Destroy();return 0; }????????繼承的時候要小心,因為指針是可能出現偏移的,繼承之后,切片可能成員位置發生變化,operator delete(this);的釋放位置就可能不對。
可變參數模板
可變參數
可變參數最早的出現是在C語言:
? ? ? ? ?以printf,不確定參數傳多少個參數,后面可以傳一串值,也就可變參數,可以有0 ~ n個參數。底層是用數組實現的。
可變參數模板
下面就是一個基本可變參數的函數模板: // Args是一個模板參數包,args是一個函數形參參數包 // 聲明一個參數包Args...args,這個參數包中可以包含0到任意個模板參數。 template <class ...Args> void ShowList(Args... args) {}// (不一定非要寫作:Args、args,可以換一個名字,只是這兩個常用) #include <string>// 可變參數的函數模板 template <class ...Args> void ShowList(Args... args) {}int main() {std::string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0; } ????????上面的參數args前面有省略號,所以它就是一個可變模版參數,我們把帶省略號的參數稱為“參數包”,它里面包含了0到N(N>=0)個模版參數。我們無法直接獲取參數包args中的每個參數的,只能通過展開參數包的方式來獲取參數包中的每個參數,這是使用可變模版參數的一個主要特點,也是最大的難點,即如何展開可變模版參數。 ? ? ? ? 如果,我們想拿到參數包里面的參數,是不好拿的, sizeof 可以幫助我們算參數包里面有多少個參數: #include <string> #include <iostream>// 可變參數的函數模板 template <class ...Args> void ShowList(Args... args) {std::cout << sizeof...(args) << std::endl; }int main() {std::string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0; } Note: for(int i = 0; i< sizeof...(args); i++) {std::cout << args[i] << " "; // error:args[i]不支持 }? ? ? ? 語法不支持使用args[i]這樣方式獲取可變參數,所以我們需要用一些奇招來 一一 獲取參數包的值。
第一種:遞歸函數方式展開參數包
? ? ? ? 將參數包改一改,增加一個參數。
#include <iostream> #include <string>// 遞歸終止函數 template <class T> void ShowList() {std::cout << std::endl; }// 展開函數 template <class T, class... Args> void ShowList(const T& value, Args... args) // 第一個參數傳給value,剩下的傳給參數包args。 {cout << value << " ";ShowList(args...); // 參數超過0個遞歸調自己,參數0個調遞歸終止函數。 }int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0; }? ? ? ? 利用遞歸不斷地推出參數包中的內容。
第二種:逗號表達式展開參數包
????????這種展開參數包的方式,不需要通過遞歸終止函數,是直接在ShowList函數體中展開的, PrintArg不是一個遞歸終止函數,只是一個處理參數包中每一個參數的函數。這種就地展開參數包的方式實現的關鍵是逗號表達式,因為逗號表達式會按順序執行逗號前面的表達式。
#include <iostream> #include <string> template <class T> void PrintArg(cosnt T t) {std::cout << t << " "; }// 展開函數 template <class... Args> void ShowList(Args... args) {// 利用逗號表達式去初始化arr,arr編譯的時候就會知道要開多大,這個時候就會依次展開args參數包。// 利用逗號表達式去取右邊的值0。(逗號表達式會按順序執行逗號前面的表達式)int arr[] = {(PrintArg(args), 0)...};std::cout << std::endl; }int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0; }同理,也可以優化為不適用逗號表達式展開參數包:
#include <iostream> #include <string> template <class T> int PrintArg(cosnt T t) {std::cout << t << " ";return 0; }// 展開函數 template <class... Args> void ShowList(Args... args) {// arr編譯的時候就會知道要開多大,這個時候就會依次展開args參數包。int arr[] = { PrintArg(args)... };std::cout << std::endl; }int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0; }empalce
????????分析STL容器中的empalce相關接口函數:
https://cplusplus.com/reference/vector/vector/emplace/
https://cplusplus.com/reference/vector/vector/emplace_back/ https://cplusplus.com/reference/list/list/emplace_back/ 以vector容器的emplace_back為例:?? ? ? ? ?emplace_back是在一個函數模板里面,把一個成員函數是實現成可變參數包。其就是通過將可變參數包不斷不斷的往下傳,傳到最下面去初始化對應數據,或者是鏈表的話就初始化節點里的數據。
template <class... Args> void emplace_back (Args&&... args); ????????首先我們看到的emplace系列的接口,支持模板的可變參數,并且萬能引用/引用折疊 (即可以引用左值,也可以引用右值) 。 #問:那么相對insert 和emplace系列接口的優勢到底在哪里呢? // vector::emplace_back #include <iostream> #include <vector>int main () {std::vector<int> myvector;myvector.push_back(100);myvector.emplace_back(200);return 0; } 如果只是簡單的int的,其與push_back就沒有什么區別。主要的區別在于: // vector::emplace_back #include <iostream> #include <vector> #include <string> #include <utility>int main() {std::vector<std::pair<std::string, int>> myvector;myvector.push_back(std::make_pair("sort", 1));myvector.emplace_back(std::make_pair("sort", 1));myvector.emplace_back("sort", 1);return 0; }? ? ? ?效率上就emplace_back更好,因為make_pair是先構造,構造了一個pair。如此push_back就傳了一個pair對象。所以調push_back是:
- 左值:構造 + 拷貝構造。
- 右值:構造 + 移動構造。
????????emplace_back是不用著急創建pair對象,我們可將這個參數包一直向下傳遞,直到最后需要插入數據的時候,直接用這個數據包創建pair對象。
- 直接構造。
????????所以emplace系列比insert系列接口不一定高效。
通過代碼凸顯區別:
? ? ? ? 不一定所有容器都會出現,于源碼的實現有關系,此處使用list容器,并在VS2019實現出來的:
#include <iostream> #include <list> #include <string>class Date { public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){std::cout << "Date(int year = 1, int month = 1, int day = 1)" << std::endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){std::cout << "Date(const Date& d)" << std::endl;}private:int _year;int _month;int _day; };int main() {std::list<Date> lt1;lt1.push_back(Date(2022, 11, 16));std::cout << "---------------------------------" << std::endl;lt1.emplace_back(2022, 11, 16);return 0; }? ? ? ? 所以建議:這個這種場景下直接使用emplace系列接口。
lambda表達式
????????lambda也叫做匿名函數。
像函數使用的對象 / 類型:
?C++98中的一個例子
? ? ? ? 因為由于仿函數有諸多的不便。如果待排序元素為自定義類型,需要用戶定義排序時的比較規則,對于以下的三個成員一個就要創建2個(less、greater),就是6個。
#include <string>struct Goods {std::string _name; // 名字double _price; // 價格int _evaluate; // 評價Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){} }; ????????隨著C++語法的發展,人們開始覺得上面的寫法太復雜了,每次為了實現一個algorithm算法,都要重新去寫一個類,如果每次比較的邏輯不一樣,還要去實現多個類,特別是相同類的命名,這些都給編程者帶來了極大的不便。因此,在C++11語法中出現了Lambda表達式。lambda表達式
lambda表達式語法
lambda表達式書寫格式:[capture-list] (parameters) mutable -> return-type { statement?}
1. lambda表達式各部分說明:- [capture-list] :?捕捉列表。該列表總是出現在lambda函數的開始位置,編譯器根據[]來判斷接下來的代碼是否為lambda函數,捕捉列表能夠捕捉上下文中的變量供lambda函數使用。
- (parameters):參數列表。與普通函數的參數列表一致,如果不需要參數傳遞,則可以連同()一起省略(無參時可以省略)
- mutable:默認情況下,lambda函數總是一個const函數,mutable可以取消其常量性。使用該修飾符時,參數列表不可省略(即使參數為空)。
- ->returntype:返回值類型。用追蹤返回類型形式聲明函數的返回值類型,沒有返回值時此部分可省略。返回值類型明確情況下,也可省略,由編譯器對返回類型進行推導。
- {statement}:函數體。在該函數體內,除了可以使用其參數外,還可以使用所有捕獲到的變量。
注意:
????????在lambda函數定義中,參數列表和返回值類型都是可選部分,而捕捉列表和函數體可以為 空。因此C++11中最簡單的lambda函數為: []{}; 該lambda函數不能做任何事情。 #include <iostream>int main() {// 兩個數相加的lambda// 沒有函數名,加一個捕捉列表[]而已。因為沒有名字,所以調用不好調// 但是[](int a, int b) -> int{ return a + b; }整體是一個對象,所以就可以巧用auto。auto add1 = [](int a, int b) -> int{ return a + b; };std::cout << add1(1, 2) << std::endl;// 省略返回值auto add2 = [](int a, int b){ return a + b; };std::cout << add2(1, 2) << std::endl; }????????于是對于前面的三個成員一個就要創建2個(less、greater),就是6個。解決:
#include <string> #include <vector> #include <algorithm>struct Goods {std::string _name; // 名字double _price; // 價格int _evaluate; // 評價//...Goods(const char *str, double price, int evaluate): _name(str), _price(price), _evaluate(evaluate){} };int main() {std::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._name < g2._name; });sort(v.begin(), v.end(), [](const Goods &g1, const Goods &g2){ return g1._name > g2._name; });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; }); }#問:如何寫一個交換swap函數?
? ? ? ? 可以像上面那樣寫,但是會非常的難看。
#include <iostream>int main() {// 交換變量的lambda - 行數會多int x = 0, y = 1;auto swap1 = [](int &x1, int &x2) -> void{int tmp = x1; x1 = x2; x2 = tmp; };swap1(x, y);std::cout << x << ":" << y << std::endl; }????????我們可以這樣寫:
#include <iostream>int main() {// 交換變量的lambda - 行數會多int x = 0, y = 1;auto swap1 = [](int &x1, int &x2) -> void{int tmp = x1; x1 = x2; x2 = tmp; };swap1(x, y);std::cout << x << ":" << y << std::endl; }捕獲列表
#問:假如我們想不傳參數交換x,y呢?
利用捕捉列表實現,注意:
- 想捕捉誰就寫誰,只能捕捉跟lambda表達式同一個作用域的對象。
- 默認捕捉過來的變量不能修改 —— 加mutable讓捕捉過來的變量可以修改(使用mutable必須加())。
- 默認捕捉是拷貝的方式捕捉,嚴格意義上說是傳值捕捉。(lambda還是一個函數調用,是有棧幀的 —— 可以理解為:改變形參,不會改變實參)
????????所以mutable在實際中不起價值作用。
捕獲列表說明:
捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。
- [var]:表示值傳遞方式捕捉變量var。
- [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)。
- [&var]:表示引用傳遞捕捉變量var。
- [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)。
- [this]:表示值傳遞方式捕捉當前的this指針。
注意:
- 父作用域指包含lambda函數的語句塊
- 語法上捕捉列表可由多個捕捉項組成,并以逗號分割。
- 比如:
- [=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量。
- [&, a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量。
- 比如:
- 捕捉列表不允許變量重復傳遞,否則就會導致編譯錯誤。
- 比如:[=, a]:=已經以值傳遞方式捕捉了所有變量,捕捉a重復
- 在塊作用域以外的lambda函數捕捉列表必須為空。
- 在塊作用域中的lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會導致編譯報錯。
- lambda表達式之間不能相互賦值,即使看起來類型相同
lambda表達底層
????????函數對象,又稱為仿函數,即可以想函數一樣使用的對象,就是在類中重載了operator()運算符的類對象,與范圍for很像。
范圍for:
? ? ? ? 并沒有看起來這么的智能,實際上是底層運用迭代器實現的。
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);r1(10000, 2);// lamberauto r2 = [=](double monty, int year) -> double{return monty * rate * year;};r2(10000, 2);return 0; }? ? ? ? 仿函數的名稱就是:lambda_uuid。所以lambda表達式對于我們是匿名的,對于編譯器而言是有名的。實際在底層編譯器對于lambda表達式的處理方式,完全就是按照函數對象的方式處理的,即:如果定義了一個lambda表達式,編譯器會自動生成一個類,在該類中重載了operator()。
包裝器
function包裝器
???? ????function包裝器也叫作適配器,C++中的function本質是一個類模板,也是一個包裝器。 #include <iostream>template <class F, class T> T useF(F f, T x) {static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x); } double f(double i) {return i / 2; } struct Functor {double operator()(double d){return d / 3;} }; int main() {// 函數名std::cout << useF(f, 11.11) << std::endl;// 仿函數對象std::cout << useF(Functor(), 11.11) << std::endl;// lamber表達式對象std::cout << useF([](double d)->double{ return d/4; }, 11.11) << std::endl;return 0; }????????因為上述的 f 的類型不同,于是會被實例化成三個。
????????包裝器可以很好的解決上面的問題,將其變為1份。
std::function在頭文件 < functional > - // 類模板原型如下 template < class T > function ; ? ? // undefined template < class Ret , class ... Args > class function < Ret ( Args ...) > ;-
模板參數說明:
Ret : 被調用函數的返回類型 Args…:被調用函數的形參 使用方法: // 使用方法如下: #include <functional> #include <iostream>int f(int a, int b) {return a + b; }struct Functor { public:int operator()(int a, int b){return a + b;} };int main() {// 函數名(函數指針)std::function<int(int, int)> func1 = f;std::cout << func1(1, 2) << std::endl;// 函數對象std::function<int(int, int)> func2 = Functor();std::cout << func2(1, 2) << std::endl;// lamber表達式std::function<int(int, int)> func3 = [](const int a, const int b){ return a + b; };std::cout << func3(1, 2) << std::endl;return 0; }? ? ? ? 對于靜態成員函數與非靜態成員函數的不同:
//使用方法如下: #include <functional> #include <iostream>class Plus { public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;} };int main() {//類的成員函數 -- 語法規定// 靜態成員函數可以不用加&,可以加&。并且可以直接調用。std::function<int(int, int)> func4 = Plus::plusi; std::cout << func4(1, 2) << std::endl;// 非靜態成員函數需要加&,并且不能直接調用,需要傳對象,此處為Plus。(成員函數多傳一個)std::function<double(Plus, double, double)> func5 = &Plus::plusd; std::cout << func5(Plus(), 1.1, 2.2) << std::endl;return 0; }????????如果對于非靜態成員函數,不想多傳一個類對象的參數,可以通過綁定的方式解決這個問題。
? ? ? ? 所以對于上面的,因為上述的 f 的類型不同,于是會被實例化成三個,就可以解決了:
#include <iostream> #include <functional>template <class F, class T> T useF(F f, T x) {static int count = 0;std::cout << "count:" << ++count << std::endl;std::cout << "count:" << &count << std::endl;return f(x); } double f(double i) {return i / 2; } struct Functor {double operator()(double d){return d / 3;} };int main() {// 函數指針std::function<double(double)> f1 = f;std::cout << useF(f1, 11.11) << std::endl;// 函數對象std::function<double(double)> f2 = Functor();std::cout << useF(f2, 11.11) << std::endl;// lamber表達式對象std::function<double(double)> f3 = [](double d)->double{ return d / 4; };std::cout << useF(f3, 11.11) << std::endl;return 0; }包裝器的其他一些場景:
?
bind綁定
????????std::bind函數定義在頭文件中,是一個函數模板,它就像一個函數包裝器(適配器),接受一個可調用對象(callable object),生成一個新的可調用對象來“適應”原對象的參數列表。一般而言,我們用它可以把一個原本接收N個參數的函數fn,通過綁定一些參數,返回一個接收M個(M可以大于N,但這么做沒什么意義)參數的新函數。同時,使用std::bind函數還可以實現參數順序調整等操作。
// 原型如下: template <class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args);// with return type (2) template <class Ret, class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args);調用bind的一般形式:auto newCallable = bind(callable,arg_list);
庫中就是使用了placeholders來占位:
https://legacy.cplusplus.com/reference/functional/placeholders/
? ? ? ? 其中的_1、_2、_3等,就是用來占位的。_1代表第1個參數,_2代表第2個參數……。調整的是形參的順序。
#include <functional> #include <iostream>int Div(int a, int b) {return a / b; }int main() {int x = 10, y = 2;std::cout << Div(x, y) << std::endl;// 調整順序 -- 雞肋,一般用不上// _1, _2.... 定義在placeholders命名空間中,代表綁定函數對象的形參,// _1,_2... 分別代表第一個形參、第二個形參...//std::function<int(int, int)> bindFunc = bind(Div, std::placeholders::_2, std::placeholders::_1);auto bindFunc = bind(Div, std::placeholders::_2, std::placeholders::_1);// 傳時候不會變std::cout << bindFunc(x, y) << std::endl;return 0; }可以理解為:
// x -> _1 ->a // y?-> _2?->b。 auto bindFunc =?bind(Div, _1, _2); bindFunc(x, y);// x -> _2 ->b // y?-> _1?->a。 auto bindFunc =?bind(Div, _2, _1); bindFunc(x, y);? ? ? ? 可以用綁定解決前面的非靜態成員函數,需要傳類對象(成員函數多傳一個),以綁定參數解決 -> 調整個數。
#include <functional> #include <iostream> #include <map>int Plus(int a, int b) {return a + b; }int Mul(int a, int b, double rate) {return a * b * rate; }class Sub { public:int sub(int a, int b){return a - b;} };// 11:50繼續 int main() {// 調整個數, 綁定死固定參數std::function<int(int, int)> funcPlus = Plus;// 本來要傳3個.// function<int(Sub, int, int)> funcSub = &Sub::sub;// 將其變為只傳2個,將1個(此處Sub())固定在這個地方綁死 — 不能變。std::function<int(int, int)> funcSub = std::bind(&Sub::sub, Sub(), std::placeholders::_1, std::placeholders::_2);// 1.5就固定死了std::function<int(int, int)> funcMul = std::bind(Mul, std::placeholders::_1, std::placeholders::_2, 1.5);std::map<std::string, std::function<int(int, int)>> opFuncMap = {{ "+", Plus},{ "-", std::bind(&Sub::sub, Sub(), std::placeholders::_1, std::placeholders::_2)}};std::cout << funcPlus(1, 2) << std::endl;std::cout << funcMul(2, 2) << std::endl;std::cout << funcSub(1, 2) << std::endl;std::cout << opFuncMap["+"](1, 2) << std::endl;std::cout << opFuncMap["-"](1, 2) << std::endl;return 0; }總結
以上是生活随笔為你收集整理的【C++】-- C++11基础常用知识点(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: The Moon and Sixpenc
- 下一篇: 这些华为技巧,花粉都不一定全知道