c++11:可調用對象

文章目錄

  • 引言
  • 1.普通函數
  • 2.函數指針
  • 3.函數對象(仿函數)
  • 4.Lambda表達式(匿名函數)
  • 5.function
  • 6.bind

引言

可調用對象是C++11引入的新概念,可以像函數調用方式的觸發調用的對象就是可調用對象。
c++98可調用對象(普通函數,函數指針,仿函數)
c++11可調用對象(普通函數,函數指針,仿函數,bind生成對象,lambda表達式,function對象)

1.普通函數

bool isEven(int value)
{return (0 == value%2)
}
vector<int> vec{2,5,9,10,11};
auto res = find_if(vec.begin(), vec.end(),isEven);
isEven 就是普通函數可調用對象

2.函數指針

函數指針就是指向普通函數的指針,一般可以理解為函數的引用,不同之處在于一個函數指針可以指向多個普通函數,
只要兩者具有相同的函數簽名格式(即相同的輸入輸出參數,返回值)

/*----------例一-------------------*/
void print_num(int num)
{cout<<"num="<<num;
}
int main()
{void (void*funPtr)(int) = &print_num;funPtr(666);//調用函數指針return 0;
}/*----------例二-------------------*/
bool isEven(int value)
{return (0 == value % 2);
}
//這是一個類型別名定義,EvenDecide被定義為一個函數指針類型,它可以接受一個整數參數并返回布爾值的函數
using EvenDecide = bool(*)(int value);std::vector<int> vec{ 2, 5, 9, 10, 11 };
//這行代碼創建了一個函數指針變量eventDecie,并將函數isEven的地址賦值給它,這樣,evenDecide就可以用來調用isEven函數
EvenDecide evenDecide = isEven;auto res = std::find_if(vec.begin(), vec.end(), evenDecide);

3.函數對象(仿函數)

重載操作符()的類 ,其對象常稱為函數對象
函數對象使用重載的()時,行為類似函數調用,因此也叫仿函數

仿函數是一個類或結構體,它重載了調用運算符operator()。
通過重載這個運算符 我們可以創建一個可調用的對象,就像函數一樣使用它。
仿函數提供了一種將函數調用 和面向對象編程結合起來的方式。
使用仿函數,我們可以將其作為參數傳遞給函數,算法或容器,并在需要的時候 通過運算符來執行相應的操作,它們可以存儲狀態,實現自定義行為并具有更高的靈活性和可定制性。
使用
函數對象可以像普通函數那樣調用,可以有參數和返回值
函數對象的優點
函數對象可以有自己的狀態(即對象的成員變量)
函數對象可以作為參數傳遞

/*----------------例一-----------------*/
#include <iostream>
using namespace std;
struct MultiplyBy
{MultiplyBy(int fac) :factor(fac) {}int operator()(int num) {return num * factor;}int factor;
};int main()
{MultiplyBy multiby(10);cout<<"multiby(5)="<<multiby(5);//50return 0;
}/*----------------例二-----------------*/
class EvenJudge
{
public:bool operator() (int value){return (0 == value % 2);}
};std::vector<int> vec{ 2, 5, 9, 10, 11 };EvenJudge evenJudge;
// 1. 通過構造臨時對象,調用operator()重載函數
auto res1 = std::find_if(vec.begin(), vec.end(), EvenJudge());
// 2. 通過對象,隱式調用調用operator()重載函數
auto res2 = std::find_if(vec.begin(), vec.end(), evenJudge);
// 3. 通過對象,顯示調用operator()重載函數
auto res3 = std::find_if(vec.begin(), vec.end(), [&evenJudge](int value) {return evenJudge.operator()(value);
});

4.Lambda表達式(匿名函數)

是一種定義內聯函數的方式,與普通函數不同,匿名函數不需要命名,可以直接在需要的地方定義和使用。
lambda表達式 常用于編寫簡潔,簡單的函數,并且可以輕松的捕獲周圍作用域中的變量。它們非常適用于需要一個簡單,臨時的回調函數或謂詞的情況。

lambda表達式語法
[capture List]->return_type(parameters){body}
//因為lambda表達式會自動推導返回值類型,所以->return_type可以省略
int main()
{int x=5;int y=6;auto sum=[x,&y](){return x+y;}int result = sum();cout<<"Sum="<<result;return 0;
}/*----------------例二-------------------------*/
auto fnIsEven = [](int value) -> bool {return (0 == value % 2);
};auto res4 = std::find_if(vec.begin(), vec.end(), fnIsEven);
auto res5 = std::find_if(vec.begin(), vec.end(), [](int value) {return(0 == value % 2);
});

在上述示例中,Lambda 表達式 x, &y{ return x + y; } 捕獲了變量 x(按值)和 y(按引用),并返回它們的和。通過調用 sum(),可以獲取到捕獲變量的求和結果。
匿名函數提供了一種更簡潔、靈活的方式來編寫內聯函數,特別適合于需要臨時使用的函數功能。

5.function

std::function是一個可調用對象包裝器,是一個類模板,可以容納除了類成員函數指針之外的所有可調用對象,它可以用統一的方式處理函數、函數對象、函數指針,并允許保存和延遲它們的執行。

/*-------------例一-------------*/
#include <functional>
void myFunction(int x) {std::cout << "Value: " << x << std::endl;
}struct MyFunctor {void operator()(int x) const {std::cout << "Value: " << x << std::endl;}
};int main() {std::function<void(int)> func;// 包裝函數指針func = &myFunction;func(42);// 包裝仿函數對象MyFunctor myFunc;func = myFunc;func(42);return 0;
}/*-------------例二-------------*/
# include <iostream>
# include <functional>typedef std::function<int(int, int)> comfun;// 普通函數
int add(int a, int b) { return a + b; }// lambda表達式
auto mod = [](int a, int b){ return a % b; };// 函數對象類
struct divide{int operator()(int denominator, int divisor){return denominator/divisor;}
};int main(){comfun a = add;comfun b = mod;comfun c = divide();std::cout << a(5, 3) << std::endl;std::cout << b(5, 3) << std::endl;std::cout << c(5, 3) << std::endl;
}

std::function可以取代函數指針的作用,因為它可以延遲函數的執行,特別適合作為回調函數使用。它比普通函數指針更加的靈活和便利。
故而,std::function的作用可以歸結于:
1.std::function對C++中各種可調用實體(普通函數、Lambda表達式、函數指針、以及其它函數對象等)的封裝,形成一個新的可調用的std::function對象,簡化調用;
2.std::function對象是對C++中現有的可調用實體的一種類型安全的包裹(如:函數指針這類可調用實體,是類型不安全的)。

6.bind

std::bind可以看作一個通用的函數適配器,它接受一個可調用對象,生成一個新的可調用對象來適應原對象的參數列表。

std::bind將可調用對象與其參數一起進行綁定,綁定后的結果可以使用std::function保存。std::bind主要有以下兩個作用:

將可調用對象和其參數綁定成一個仿函數;
只綁定部分參數,減少可調用對象傳入的參數。

調用bind的一般形式:

auto newCallable = bind(callable, arg_list);

該形式表達的意思是:當調用newCallable時,會調用callable,并傳給它arg_list中的參數。

需要注意的是:arg_list中的參數可能包含形如_n的名字。其中n是一個整數,這些參數是占位符,表示newCallable的參數,它們占據了傳遞給newCallable的參數的位置。數值n表示生成的可調用對象中參數的位置:_1為newCallable的第一個參數,_2為第二個參數,以此類推。

#include <iostream>
#include <functional>class A {
public:void fun_3(int k,int m) {std::cout << "print: k = "<< k << ", m = " << m << std::endl;}
};void fun_1(int x,int y,int z) {std::cout << "print: x = " << x << ", y = " << y << ", z = " << z << std::endl;
}void fun_2(int &a,int &b) {++a;++b;std::cout << "print: a = " << a << ", b = " << b << std::endl;
}int main(int argc, char * argv[]) {//f1的類型為 function<void(int, int, int)>auto f1 = std::bind(fun_1, 1, 2, 3); 					//表示綁定函數 fun 的第一,二,三個參數值為: 1 2 3f1(); 													//print: x=1,y=2,z=3auto f2 = std::bind(fun_1, std::placeholders::_1, std::placeholders::_2, 3);//表示綁定函數 fun 的第三個參數為 3,而fun 的第一,二個參數分別由調用 f2 的第一,二個參數指定f2(1, 2);												//print: x=1,y=2,z=3auto f3 = std::bind(fun_1, std::placeholders::_2, std::placeholders::_1, 3);//表示綁定函數 fun 的第三個參數為 3,而fun 的第一,二個參數分別由調用 f3 的第二,一個參數指定//注意: f2  和  f3 的區別。f3(1, 2);												//print: x=2,y=1,z=3int m = 2;int n = 3;auto f4 = std::bind(fun_2, std::placeholders::_1, n); //表示綁定fun_2的第一個參數為n, fun_2的第二個參數由調用f4的第一個參數(_1)指定。f4(m); 													//print: a=3,b=4std::cout << "m = " << m << std::endl;					//m=3  說明:bind對于不事先綁定的參數,通過std::placeholders傳遞的參數是通過引用傳遞的,如mstd::cout << "n = " << n << std::endl;					//n=3  說明:bind對于預先綁定的函數參數是通過值傳遞的,如nA a;//f5的類型為 function<void(int, int)>auto f5 = std::bind(&A::fun_3, &a, std::placeholders::_1, std::placeholders::_2); //使用auto關鍵字f5(10, 20);												//調用a.fun_3(10,20),print: k=10,m=20std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);fc(10, 20);   											//調用a.fun_3(10,20) print: k=10,m=20 return 0; 
}

編譯并運行:

print: x = 1, y = 2, z = 3
print: x = 1, y = 2, z = 3
print: x = 2, y = 1, z = 3
print: a = 3, b = 4
m = 3
n = 3
print: k = 10, m = 20
print: k = 10, m = 20

由此例子可以看出:

  • 預綁定的參數是以值傳遞的形式,不預綁定的參數要用std::placeholders(占位符)的形式占位,從_1開始,依次遞增,是以引用傳遞的形式;
  • std::placeholders表示新的可調用對象的第幾個參數,而且與原函數的該占位符所在位置的進行匹配;
  • bind綁定類成員函數時,第一個參數表示對象的成員函數的指針,第二個參數表示對象的地址,這是因為對象的成員函數需要有this指針。并且編譯器不會將對象的成員函數隱式轉換成函數指針,需要通過&手動轉換;
  • std::bind的返回值是可調用實體,可以直接賦給std::function。

參考一

參考二

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/697900.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697900.shtml
英文地址,請注明出處:http://en.pswp.cn/news/697900.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java設計模式【代理模式】

一、前言 1.1 背景 在不改變原有代碼的基礎上&#xff0c;對方法進行功能性的增強&#xff1b; 1.2 簡介 代理模式是一種結構型模式&#xff0c;為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下&#xff0c;一個對象不想或者不能直接引用另一個對象&#xff0…

axure9.0 工具使用思考

原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】快速原型設計工具原型設計軟件【AxureRP】…

CentOS使用Docker搭建Halo網站并實現無公網ip遠程訪問

&#x1f525;博客主頁&#xff1a; 小羊失眠啦. &#x1f3a5;系列專欄&#xff1a;《C語言》 《數據結構》 《C》 《Linux》 《Cpolar》 ??感謝大家點贊&#x1f44d;收藏?評論?? 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&…

【華為OD機試真題 C++語言】483、中文分詞模擬器 | 機試真題+思路參考+代碼解析(C卷)

文章目錄 一、題目??題目描述??輸入輸出??樣例1??樣例2??樣例3二、思路參考三、代碼參考作者:KJ.JK??個人博客首頁: KJ.JK ??專欄介紹: 華為OD機試真題匯總,定期更新華為OD各個時間階段的機試真題,每日定時更新,本專欄將使用C++語言進行更新解答,包含真…

創紀錄:英偉達市值一日增 2770 億美元;Xiaomi 14 Ultra 正式發布丨 RTE 開發者日報 Vol.150

開發者朋友們大家好&#xff1a; 這里是 「RTE 開發者日報」 &#xff0c;每天和大家一起看新聞、聊八卦。我們的社區編輯團隊會整理分享 RTE &#xff08;Real Time Engagement&#xff09; 領域內「有話題的 新聞 」、「有態度的 觀點 」、「有意思的 數據 」、「有思考的 文…

mysql 用戶權限管理

mysql使用系統庫mysql的user表來存儲用戶信息。mysql.user表詳細的記錄了用戶名&#xff0c;對應的允許連接的主機信息還有各種全局權限標識位。 用戶管理 創建用戶 CREATE USER 用戶名host主機 IDENTIFIED BY 密碼;上面是創建用的基本命令&#xff0c;指定了用戶名&#xf…

Selenium基礎知識

一、環境搭建&#xff08;以java為例&#xff09; 1.下載chrome瀏覽器 https://www.google.cn/intl/zh-CN/chrome/ 2.查看chrome瀏覽器版本 設置關于chrome 3.下載chrome瀏覽器驅動 下載瀏覽器對應版本的 ChromeDriver - WebDriver for Chrome - Downloads 120以上版本&…

WordPress使用

WordPress功能菜單 儀表盤 可以查看網站基本信息和內容。 文章 用來管理文章內容&#xff0c;分類以及標簽。編輯文章以及設置分類標簽&#xff0c;分類和標簽可以被添加到 外觀-菜單 中。 分類名稱自定義&#xff1b;別名為網頁url鏈接中的一部分&#xff0c;最好別設置為中文…

概率密度函數(PDF)與神經網絡中的激活函數

原創:項道德(daode3056,daode1212) 在量子力學中&#xff0c;許多現象都是統計的結果&#xff0c;基本上用的是正態分布&#xff0c;然而&#xff0c;從本質上思考&#xff0c;應該還存在低階的分布&#xff0c;標準的正態分布是它的極限&#xff0c;這樣一來&#xff0c;或許在…

python中多線程使用

前言 記錄下Python中多線程使用 標題 前言簡介使用demo 簡介 Python 中的多線程主要通過 threading 模塊來實現。多線程是一種并發編程的方式&#xff0c;允許程序在同一時間執行多個線程&#xff0c;每個線程執行不同的任務。然而需要注意的是&#xff0c;在 Python 中由于 …

【前端素材】推薦優質后臺管理系統Spica Admin平臺模板(附源碼)

一、需求分析 后臺管理系統是一種用于管理網站、應用程序或系統的工具&#xff0c;它通常作為一個獨立的后臺界面存在&#xff0c;供管理員或特定用戶使用。下面詳細分析后臺管理系統的定義和功能&#xff1a; 1. 定義 后臺管理系統是一個用于管理和控制網站、應用程序或系統…

【安全】大模型安全綜述

大模型相關非安全綜述 LLM演化和分類法 A survey on evaluation of large language models,” arXiv preprint arXiv:2307.03109, 2023.“A survey of large language models,” arXiv preprint arXiv:2303.18223, 2023.“A survey on llm-gernerated text detection: Necess…

刷題日記-Day1- Leedcode-704. 二分查找,27. 移除元素-Python實現

704 二分查找 鏈接&#xff1a;https://leetcode.cn/problems/binary-search/description/ 給定一個 n 個元素有序的&#xff08;升序&#xff09;整型數組 nums 和一個目標值 target &#xff0c;寫一個函數搜索 nums 中的 target&#xff0c;如果目標值存在返回下標&#xf…

vue3 toRefs之后的變量修改方法

上效果 修改值需要帶上解構之前的對象名obj&#xff0c; changeName:()>{ // toRefs 解決后變量修改值方法&#xff1a; 解構前變量.字段新值 obj.name FEIFEI; } } 案例源碼 <!DOCTYPE html> <html> <head><me…

如何在pgAdmin中用替換的值更新jsonb列?

我有一個名為files的PostgreSQL表&#xff0c;其中包括一個名為formats的jsonb表。雖然有些行是[null]&#xff0c;但其他行具有此結構的對象&#xff1a; {"thumbnail": {"ext": ".jpg","url": "https://some-url.com/image01.…

Vue | (四)使用Vue腳手架(上) | 尚硅谷Vue2.0+Vue3.0全套教程

文章目錄 &#x1f4da;初始化腳手架&#x1f407;創建初體驗&#x1f407;分析腳手架結構&#x1f407;關于render&#x1f407;查看默認配置 &#x1f4da;ref與props&#x1f407;ref屬性&#x1f407;props配置項 &#x1f4da;混入&#x1f4da;插件&#x1f4da;scoped樣…

idea配置javafx

一、下載sdk 在jdk8之后,需要下載sdk包 ??javafx-sdk-18.zip 這里適用的jkd版本如圖 二、配置 創建一個項目之后,進行如下配置,將sdk導入到項目中 配置啟動參數 可以使用-號將之前的去掉&

同步 BUCK 與 異步 BUCK 的區別

上篇文章介紹 BUCK 基本拓撲電路工作原理&#xff0c;BUCK 電路如下圖&#xff1a; 因為二極管的存在&#xff0c;只需要控制一個 MOS 管開關&#xff0c;一般將該電路稱為異步 BUCK 電路&#xff0c;如果把這個二極管換為 MOS 管&#xff0c;如下圖&#xff1a; 該電路用到了兩…

vue Threejs實現任意畫線(鼠標點擊畫線)

Threejs實現任意畫線(鼠標點擊畫線) 鼠標左鍵單擊添加點鼠標右鍵回退到上一個點,并繼續畫按住shift可以畫平行于x軸或平行于z軸的線按Esc完成畫線

【leetcode題解C++】121.買賣股票的最佳時機 and 122.買賣股票的最佳時機II and 55.跳躍游戲 and 45.跳躍游戲II

121. 買賣股票的最佳時機 給定一個數組 prices &#xff0c;它的第 i 個元素 prices[i] 表示一支給定股票第 i 天的價格。 你只能選擇 某一天 買入這只股票&#xff0c;并選擇在 未來的某一個不同的日子 賣出該股票。設計一個算法來計算你所能獲取的最大利潤。 返回你可以從…