3.C++11中引入的bind綁定器和function函數對象

来源:https://www.cnblogs.com/woden3702/archive/2022/05/23/16301307.html
-Advertisement-
Play Games

今天遇到一個很有意思的bug,當程式開發完成後打包到伺服器運行,總是會出現棧溢出異常 ...


綁定器bind1st,bind2nd

vector<int> vec;
for(int i=0;i<20;i++){
    vec.push_back(rand()%100);
}
showContainer(vec);

sort(vec.begin(),vec.end(),greater<int>());
showContainer(vec);
/*
 * greater  a>b
 * less     a<b
 * */
//把70按順序插入到vector容器中
auto it= find_if(vec.begin(), vec.end(), bind1st(greater<int>(),70));//兩種綁定器的用法
//auto it= find_if(vec.begin(), vec.end(), bind2nd(less<int>(),70));
if(it!=vec.end()){
    vec.insert(it,70);
}
showContainer(vec);

綁定器的實現原理

綁定器其實是函數對象的一個應用!!綁定器+二元函數對象+值=一元函數對象。底層還是靠二元函數對象做事

自己實現一個綁定器:

/**
 * 
 * @tparam Compare 函數對象類型
 * @tparam T 對象類型
 */
template<typename Compare,typename T>
class CMyBind1st{
public:
    CMyBind1st(Compare com,T val):_comp(com),_val(val){}
    /**
     * 綁定器函數對象的實現,實際上是傳入的函數對象在幹活
     * @param second 傳入要綁定的數據
     * @return 函數對象,這個函數對象幹完傳回的數據
     */
    bool operator()(const T &second){
        return _comp(_val,second);
    }
private:
    Compare _comp;
    T _val;
};
/**
 * 實現綁定器
 * @tparam Compare 返回函數對象類型
 * @tparam T 數據對象
 * @param comp 函數對象
 * @param val 綁定數據
 * @return 
 */
template<typename Compare,typename T>
CMyBind1st<Compare,T> my_bind1st(Compare comp,const T &val){
    return CMyBind1st<Compare,T>(comp,val);
}

/**
 *
 * @tparam Compare 函數對象類型
 * @tparam T 對象類型
 */
template<typename Compare,typename T>
class CMyBind2nd{
public:
    CMyBind2nd(Compare com,T val):_comp(com),_val(val){}
    /**
     * 綁定器函數對象的實現,實際上是傳入的函數對象在幹活
     * @param second 傳入要綁定的數據
     * @return 函數對象,這個函數對象幹完傳回的數據
     */
    bool operator()(const T &first){
        return _comp(first,_val);
    }
private:
    Compare _comp;
    T _val;
};
/**
 * 實現綁定器
 * @tparam Compare 返回函數對象類型
 * @tparam T 數據對象
 * @param comp 函數對象
 * @param val 綁定數據
 * @return 返回的是一個函數對象
 */
template<typename Compare,typename T>
CMyBind2nd<Compare,T> my_bind2nd(Compare comp,const T &val){
    return CMyBind2nd<Compare,T>(comp,val);
}
/////////////////
/**
 * 實現find_if
 * @tparam Iterator 迭代器
 * @tparam Compare 函數對象
 * @param begin 
 * @param end 
 * @param comp 函數對象
 * @return 迭代器
 */
template<typename Iterator,typename  Compare>
Iterator my_find_if(Iterator begin,Iterator end,Compare comp){
    for(;begin!=end;++begin){
        if(comp(*begin)){//comp.operator()(*begin)
            return begin;
        }
    }
    return end;
}

function函數對象的應用

首先講一下什麼是函數指針

如果在程式中定義了一個函數,那麼在編譯時系統就會為這個函數代碼分配一段存儲空間,這段存儲空間的首地址稱為這個函數的地址。而且函數名錶示的就是這個地址。既然是地址我們就可以定義一個指針變數來存放,這個指針變數就叫作函數指針變數,簡稱函數指針。

那麼這個指針變數怎麼定義呢?雖然同樣是指向一個地址,但指向函數的指針變數同我們之前講的指向變數的指針變數的定義方式是不同的。例如:

int(*p)(int, int);

這個語句就定義了一個指向函數的指針變數 p。首先它是一個指針變數,所以要有一個“”,即(p);其次前面的 int 表示這個指針變數可以指向返回值類型為 int 型的函數;後面括弧中的兩個 int 表示這個指針變數可以指向有兩個參數且都是 int 型的函數。所以合起來這個語句的意思就是:定義了一個指針變數 p,該指針變數可以指向返回值類型為 int 型,且有兩個整型參數的函數。p 的類型為 int(*)(int,int)。

所以函數指針的定義方式為:函數返回值類型 (* 指針變數名) (函數參數列表);

函數指針的使用:

int Func(int x);   /*聲明一個函數*/
int (*p) (int x);  /*定義一個函數指針*/
p = Func;          /*將Func函數的首地址賦給指針變數p*/
# include <stdio.h>
int Max(int, int);  //函數聲明
int main(void)
{
    int(*p)(int, int);  //定義一個函數指針
    int a, b, c;
    p = Max;  //把函數Max賦給指針變數p, 使p指向Max函數
    printf("please enter a and b:");
    scanf("%d%d", &a, &b);
    c = (*p)(a, b);  //通過函數指針調用Max函數
    printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
    return 0;
}
int Max(int x, int y)  //定義Max函數
{
    int z;
    if (x > y)
    {
        z = x;
    }
    else
    {
        z = y;
    }
    return z;
}

輸出結果是:
please enter a and b:3 4
a = 3
b = 4
max = 4

function:

void hello1(){
    cout<<"hello1"<<endl;
}

void hello2(string str){
    cout<<str<<endl;
}

int sum(int a,int b){
    return a+b;
}

class TT{
public:
    TT()=default;
    void hello(string str) const {
        cout<<"call TT::hello"<<str<<endl;
    }
};

int main(){

    function<void()> func1=hello1;
    function<void(string)> func2=hello2;

    func1();
    func2("sashkgf");

    function<int(int,int)> func3=sum;

    cout<<func3(20,22)<<endl;

    function<void(TT*,string)> func4=&TT::hello;
    TT t;
    func4(&t,"asgasgxb");

    return 0;
}

使用function需要註意兩點:

  1. 用函數類型實例化function
  2. 通過function調用operator()函數的時候,需要根據函數類型傳入相應的參數

一個function應用的例子:

void doShow(){cout<<"查看所有書籍"<<endl;}
void doBorrow(){cout<<"借書"<<endl;}
void doBack(){cout<<"還書"<<endl;}
void doQuery(){cout<<"查詢書籍"<<endl;}
void doLogout(){cout<<"註銷"<<endl;}


int main(){

    int choice=0;
    map<int,function<void()>> actionMap;
    actionMap.insert({1,doShow});
    actionMap.insert({2,doBorrow});
    actionMap.insert({3,doBack});
    actionMap.insert({4,doQuery});
    actionMap.insert({5,doLogout});

    for(;;){
        cout<<"————————————————————"<<endl;
        cout<<"1_查看所有書籍"<<endl;
        cout<<"2_借書"<<endl;
        cout<<"3_還書"<<endl;
        cout<<"4_查詢書籍"<<endl;
        cout<<"5_註銷"<<endl;
        cout<<"————————————————————"<<endl;
        cin>>choice;
        auto it= actionMap.find(choice);
        if(it==actionMap.end()){
            cout<<"輸入數字無效,請重新選擇"<<endl;
        }else{
            it->second();
        }

    }
    return 0;
}

輸出:


1_查看所有書籍
2_借書
3_還書
4_查詢書籍
5_註銷


4
查詢書籍

如何實現function

首先介紹兩個前置知識:

模板的完全特例化和非完全特例化

模板的實參推演

實現function

template<typename Fty>//必須要有
class MyFunction{};

template<typename R,typename... A>//非完全特例化
class MyFunction<R(A...)>{//function其實就是函數指針的封裝
public:
    using PFUNC=R(*)(A...);//函數指針
    MyFunction(PFUNC pfunc):_pfunc(pfunc){}
    R operator()(A... arg){
        return _pfunc(arg...);
    }
private:
    PFUNC _pfunc;
};

int main(){
    MyFunction<void(string)> func2=hello2;

    func2("sashkgf");

    MyFunction<int(int,int)> function= sum;
    cout<<function(20,30)<<endl;
    return 0;
}

使用bind和function實現線程池

C++11里的綁定器bind返回的還是是一個函數對象

//TODO 學完線程再回來寫這個筆記

lambda表達式

lambda表達式的語法:[捕獲外部變數](形參列表)->返回值{操作代碼}

如果lambda表達式的返回值不需要,那麼->返回值可以省略

[捕獲外部變數]

lambda表達式生成的函數對象的()重載預設是const常量形的,不能對內部的值進行修改,需要在lambda表達式的後面加mutable標識符才可以修改(只有是傳值的情況下需要加這個標識符)。如果是[&]的情況就不需要加這個標識符

使用lambda表達式實現優先順序隊列:

class Data{
public:
    Data(int val1=10,int val2=10):ma(val1),mb(val2){}
    int ma;
    int mb;
};
int main(){
    using FUNC=function<bool(Data&,Data&)>;
    priority_queue<Data,vector<Data>,FUNC> que([](Data& d1,Data& d2)->bool{//在構造函數中使用lambda表達式傳入函數對象
        return d1.ma>d2.ma;
    });
    que.push({10,20});
    que.push({20,10});
    que.push({30,40});

    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:先彙總相關股票價格,然後有選擇地對其分類,再計算移動均線、布林線等。 一、彙總數據 彙總整個交易周中從周一到周五的所有數據(包括日期、開盤價、最高價、最低價、收盤價,成交量等),由於我們的數據是從2020年8月24日開始導出,數據多達420條,先截取部分時間段的數據,不妨先讀取開始20個交易日 ...
  • EL和JSTL都是JSP的內容的拓展,都是開發的一些東西,稍微學習記錄一下,避免以後忘記 ...
  • 做這些業務設計時,核心思想是:把常用的邏輯進行封裝,流程設計為可配置,這樣即可在一定時間內應對業務的需求和變化,降低開發成本的支出,從而使研發更側重核心業務的管理和抽象封裝等內容。 ...
  • 需求是用java程式獲取txt文件中的數據並將姓名、職稱、工資添加到新txt文件中,txt文件中數據的格式是固定的,如下: 添加後的格式是這樣的: 這裡不考慮工資是怎麼算的,只說獲取數據和寫入數據的方法。 教師姓名和職稱之間是有空格的,而職稱和下一個教師之間是有換行的。 通過查閱資料,我發現了一個特 ...
  • 泛型 前言 以前學習到「泛型」的時候,只是淺淺的知道可以限制類型,並沒有更深入理解,可以說基礎的也沒理解到位,只是浮於錶面,所以,現在回爐重造,重學泛型!打好基礎! 什麼是泛型? 泛型(Generic),Generic 的意思有「一般化的,通用的」。 是 JDK 5 中引入的新特性,它提供編譯時的類 ...
  • ipchat 點對點聊天工具 1.00.05 已發佈。 zg-ipchat 是一款聊天工具。可實現簡單的文本信息傳輸,無加密。 ...
  • 《Java 面試指北》來啦!這是一份教你如何更高效地準備面試的小冊,涵蓋常見八股文(系統設計、常見框架、分散式、高併發 ......)、優質面經等內容。 本文原發於 MySQL知識點&面試題總結 。 你好,我是 Guide。分享一道群友面試蝦皮遇到的 MySQL 事務相關的面試真題。 這篇文章我除了 ...
  • Stream操作是Java 8推出的一大亮點!雖然java.util.stream很強大,但依然還是有很多開發者在實際工作中很少使用,其中吐槽最多的一個原因就是不好調試,一開始確實是這樣,因為stream這樣的流式操作在DEBUG的時候,是一行代碼,直接下一步的時候,其實一下就過去了好多操作,這樣我 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...