日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【C++】-- C++11基础常用知识点(下)

發布時間:2024/1/8 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++】-- C++11基础常用知识点(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上篇:?【C++】-- C++11基礎常用知識點(上)_川入的博客-CSDN博客


目錄

新的類功能

默認成員函數

可變參數模板

可變參數

可變參數模板

empalce

lambda表達式

C++98中的一個例子

lambda表達式

lambda表達式語法

捕獲列表

lambda表達底層

包裝器

function包裝器

bind綁定


新的類功能

默認成員函數

原來C++類中,有6個默認成員函數:
  • 構造函數
  • 析構函數
  • 拷貝構造函數
  • 拷貝賦值重載
  • 取地址重載
  • const 取地址重載
  • ????????最后重要的是前4個,后兩個用處不大。默認成員函數就是我們不寫編譯器會生成一個默認的。 C++11 新增了兩個:移動構造函數和移動賦值運算符重載。

    ? ? ? ? 所以到了C++11后有8個默認成員函數。

    移動構造函數和移動賦值運算符重載的又來以及原理:
    【C++】-- C++11 - 右值引用和移動語義(上萬字詳細配圖配代碼從執行一步步講解)_川入的博客-CSDN博客

    只有在深拷貝的情況下才會有移動構造函數和移動賦值運算符重載。可以認為:

    • 拷貝構造函數與拷貝賦值重載:針對于左值的拷貝。
    • 移動構造函數和移動賦值重載:針對于右值的拷貝。

    ????????移動構造函數和移動賦值重載,編譯器自行生成的默認成員函數,能用的條件的復雜度與苛刻程度遠遠大于:構造函數、析構函數 、拷貝構造函數 、拷貝賦值重載4個默認成員函數。(由于:取地址重載 、const 取地址重載幾乎不用自己寫,用編譯器的即可,所以忽略)

    針對移動構造函數和移動賦值運算符重載有一些需要注意的點如下:
    • 編譯器生成默認移動構造函數條件

    ????????沒有自己實現移動構造函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個。那么編譯器會自動生成一個默認移動構造。

    • 編譯器生成默認移動構造函數實現

    ????????默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝。自定義類型成員,則需要看這個成員是否實現移動構造,如果實現了就調用移動構造,沒有實現就調用拷貝構造。

    • 編譯器生成默認動賦值重載函數條件
    ????????你沒有自己實現移動賦值重載函數,且沒有實現析構函數 、拷貝構造、拷貝賦值重載中的任意一個,那么編譯器會自動生成一個默認移動賦值。
    • 編譯器生成默認動賦值重載函數實現
    ? ???????默認生成的移動構造函數,對于內置類型成員會執行逐成員按字節拷貝,自定義類型成員,則需要看這個成員是否實現移動賦 值,如果實現了就調用移動賦值,沒有實現就調用拷貝賦值。(默認移動賦值跟上面移動構造完全類似)。
    • 如果你提供了移動構造或者移動賦值,編譯器不會自動提供拷貝構造和拷貝賦值。
    強制生成默認函數的關鍵字default:
    ? ????????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++不喜歡的操作,所以有了仿函數。(全局的函數)
  • 仿函數 / 函數對象。(全局的類)
  • 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還是一個函數調用,是有棧幀的 —— 可以理解為:改變形參,不會改變實參)
    #include <iostream>int main() {// 交換變量的lambda - 行數會多int x = 0, y = 1;// 可以理解為:改變形參,不會改變實參auto swap = [x, y]()mutable{int tmp = x; x = y; y = tmp; };swap();std::cout << x << ":" << y << std::endl; }

    ????????所以mutable在實際中不起價值作用。

    捕獲列表說明:

    捕捉列表描述了上下文中那些數據可以被lambda使用,以及使用的方式傳值還是傳引用。

    • [var]:表示值傳遞方式捕捉變量var。
    • [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)。
    • [&var]:表示引用傳遞捕捉變量var。
    • [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)。
    • [this]:表示值傳遞方式捕捉當前的this指針。

    注意:

    • 父作用域指包含lambda函數的語句塊
    • 語法上捕捉列表可由多個捕捉項組成,并以逗號分割
      • 比如:
        • [=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量。
        • [&, a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量。
    • 捕捉列表不允許變量重復傳遞,否則就會導致編譯錯誤
      • 比如:[=, a]:=已經以值傳遞方式捕捉了所有變量,捕捉a重復
    • 在塊作用域以外的lambda函數捕捉列表必須為空
    • 在塊作用域中的lambda函數僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會導致編譯報錯。
    • lambda表達式之間不能相互賦值,即使看起來類型相同
    #include <iostream>void (*PF)();int main() {auto f1 = []{ std::cout << "hello world" << std::endl; };auto f2 = []{ std::cout << "hello world" << std::endl; };// f1 = f2; ? // 編譯失敗--->提示找不到operator=()// 允許使用一個lambda表達式拷貝構造一個新的副本auto f3(f2);f3();// 可以將lambda表達式賦值給相同類型的函數指針PF = f2;PF();return 0; }

    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; }

    包裝器的其他一些場景:
    ?

    class Solution { public:int evalRPN(vector<string> &tokens){stack<long long> st;map<string, function<long long(long long, long long)>> opFuncMap ={{"+", [](long long i, long long j){ return i + j; }},{"-", [](long long i, long long j){ return i - j; }},{"*", [](long long i, long long j){ return i * j; }},{"/", [](long long i, long long j){ return i / j; }}};for (auto &str : tokens){if (opFuncMap.find(str) != opFuncMap.end()){long long right = st.top();st.pop();long long left = st.top();st.pop();st.push(opFuncMap[str](left, right));}else{// 1、atoi itoa// 2、sprintf scanf// 3、stoi to_string C++11st.push(stoll(str));}}return st.top();} };

    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基础常用知识点(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。