網站首頁 編程語言 正文
本作者一致的觀點就是 在任何語言執行的時候先去思考匯編層面能不能做到 如果能做到 那么高級語言才能做到 無論你推出什么新特性 用戶態匯編你都是繞不開的 比如你要調用函數 那么你必須要使用call指令 那么就必須要有函數地址 接下來我們來詳細說說為什么c++11要推出這個新概念 以及他解決了什么問題 還有如何使用它
Tips:c++的設計哲學是你必須時刻清楚你自己在干什么 stl內部并不會給你執行任何的安全檢查 程序直接崩潰也是完全有可能的 功力不夠 就不要玩花的
為什么需要他
在c++11還沒有推出callable object的時候 那時候如果你想要把普通函數當做參數一樣傳遞那么你就只能使用函數指針 如下段代碼所示
我就不再演示如果你有函數指針如何調用函數了 空指針發生調用異常啥的也是你自己的問題那非常簡單了 不需要過多描述
注意 FuncP是一種類型 而非一個指針
#include <iostream>
void test(int i)
{
return;
}
typedef void(*FuncP)(int i);
void recevier(void(*)(int))
{
std::cout << "recevier working!" << std::endl;
}
int main()
{
recevier(&test);
FuncP ding = test;
recevier(&test);
void(*FuncP1)(int) = test;
recevier(FuncP1);
}
運行結果截圖:
而如果你想把類的成員函數傳遞 那更是麻煩 因為我們知道類的成員函數在調用的時候是需要傳遞this指針的 那么我們來看一看如何傳遞類的成員函數 如下段代碼所示
#include <iostream>
class testclass
{
public:
void MemberFunc(int i)
{
std::cout << "MemberFunc" << std::endl;
}
};
typedef void(testclass::* CFuncP)(int);
void recevier(void(testclass::* memberFunc)(int))
{
testclass p;
(p.*memberFunc)(1);
std::cout << "recevier working!" << std::endl;
}
void recevier(void(*)(int))
{
std::cout << "recevier working!" << std::endl;
}
int main()
{
recevier(&testclass::MemberFunc);
CFuncP ding = &testclass::MemberFunc;
recevier(ding);
void(testclass:: * testF)(int) = &testclass::MemberFunc;
recevier(testF);
}
運行結果如下圖
可以看到啊 雖然咱們用普通的成員函數指針和普通的函數指針似乎已經可以解決絕大部分問題 那為什么又要推出一個callable object呢? 關鍵在于這種代碼實現起來 其實可讀性很差 而且不是那么好理解 如果有一種統一的方式來讓我們可以把函數當做參數傳遞 并且能直接調用就好了 那么c++11便推出了callable object
他究竟是啥
那么究竟什么叫做callable object呢 顧名思義 就是可以被調用的對象 看如下一個最簡單的例子
#include <iostream>
class testclass
{
public:
void MemberFunc(int i)
{
std::cout << "MemberFunc" << std::endl;
}
void operator()()
{
std::cout << "callable object working" << std::endl;
}
};
int main()
{
testclass ding;
ding();
}
當一個對象 重載了() 運算符的時候 我們就把他看做 一個最簡單的callable object 因為他這個對象可以像函數一樣被調用是吧 沒有什么問題 那么接下來我們就來看看stl提供的 能讓你快速生成callable object的組件之std::bind
他怎樣被使用呢
我們先來看一個最簡單的例子 基于std::bind
void test(int i, int j, int k)
{
std::cout << "test working!" << std::endl;
std::cout << i << std::endl;
std::cout << j << std::endl;
std::cout << k << std::endl;
}
int main()
{
std::bind(&test, 1, 2, 3)();
}
如上圖所示 我們使用std::bind來創建了一個callable object并且在創建這個callable object的時候就已經把他三個參數傳遞好了 這點非常重要 這會影響到接下來的占位符的講解 然后馬上使用()來調用了他 運行結果如下圖所示
相比較于普通的函數指針 這樣的方式是不是更簡單明了了呢? 當然這遠遠不是他的全部 接下來我們來看看使用占位符的時候 該如何使用它 代碼如下圖所示
void test(int i, int j, int k)
{
std::cout << "test working!" << std::endl;
std::cout << i << std::endl;
std::cout << j << std::endl;
std::cout << k << std::endl;
}
int main()
{
std::bind(&test, 1,std::placeholders::_1,std::placeholders::_2)(2,3);
}
可以看到上圖 我們在創建callable object的時候 并沒有去傳遞全部的參數 而是使用了占位符 然后在真正調用的時候才去傳遞了2和3兩個參數 運行結果如下圖所示
下面讓我們升華到成員函數好嘛? 不過多講解 咱們直接看代碼
#include <iostream>
#include <functional>
class testclass
{
public:
void MemberFunc(int i,int j,int k)
{
std::cout << "MemberFunc" << std::endl;
std::cout << i << std::endl;
std::cout << j << std::endl;
std::cout << k << std::endl;
}
void operator()()
{
std::cout << "callable object working" << std::endl;
}
};
int main()
{
std::bind(&testclass::MemberFunc, testclass(), 1, std::placeholders::_1, std::placeholders::_2)(2, 3);
}
成員函數的調用時需要this指針的 我相信大家都知道 那么這就是為什么我在std::bind的第二個參數創建了一個臨時對象 用來傳遞this指針 可以看到相比較于普通的成員函數指針 這種方式明顯要方便的多 注意啊 使用std::bind來創建成員函數的callable object的時候 第二個參數必須是對象或者某個對象的this指針 不然直接會G的
亂玩導致的G 你自己負責 如下圖所示
當然 像下圖這樣先使用占位符將傳入對象暫定 然后在參數中去傳遞對象或this指針也是可以的
std::bind
(&testclass::MemberFunc,std::placeholders::_1, 1, std::placeholders::_2,
std::placeholders::_3)(testclass(),2, 3);
這個時候你就要問了 OK 這樣確實很方便 沒錯 但是你說了這么久也沒見你把它當做參數來傳遞啊 下面就來介紹std::function 他不僅能當做參數來傳遞 并且還可以保存callable object 屬實是非常方便 老樣子 還是從最簡單的例子開始 代碼如下圖
#include <iostream>
#include <functional>
class testclass
{
public:
int ding{};
void MemberFunc(int i,int j,int k)
{
std::cout << "MemberFunc" << std::endl;
std::cout << i << std::endl;
std::cout << j << std::endl;
std::cout << k << std::endl;
std::cout << ding << std::endl;
}
void operator()()
{
std::cout << "callable object working" << std::endl;
}
};
int main()
{
std::function<void(int,int,int)>MemberCall=std::bind(&testclass::MemberFunc,testclass(), 1, std::placeholders::_1, std::placeholders::_2);
MemberCall(1,2,NULL);
std::function<void(testclass&, int, int, int) > MemberCall2=&testclass::MemberFunc;
testclass C;
MemberCall2(C, 1, 1, 1);
}
如上圖所示 因為我已經放置了一個占位符 所以呢 第三個參數無論你填什么 都是沒用的 只會取前兩個參數 調用結果如下所示:
我們再來試試將他作為參數傳遞 很簡單咯 玩起來
void test(std::function<void(int, int, int)>& Func, int c)
{
Func(1, 2, 3);
std::cout << c<<std::endl;
}
int main()
{
std::function<void(int,int,int)>MemberCall=std::bind(&testclass::MemberFunc,testclass(), 1, std::placeholders::_1, std::placeholders::_2);
MemberCall(1,2,NULL);
std::function<void(testclass&, int, int, int) > MemberCall2=&testclass::MemberFunc;
testclass C;
MemberCall2(C, 1, 1, 1);
test(MemberCall, 3);
}
運行結果如下:
看看 有了std::function和std::bind 函數就像對象一樣可以被傳遞保存和調用 是不是很方便呢
Tips:既然他是一個對象了 那任何對對象的操作對于他都是允許的 比如塞入隊列啥的 可以任意交互 其他東西我就不演示了 對于對象的各種操作是基本知識
最后讓我們來看看他跟lambda之間的化學反應 代碼如下:
#include <iostream>
#include <functional>
class testclass
{
public:
int ding{};
void MemberFunc(int i,int j,int k)
{
std::cout << "MemberFunc" << std::endl;
std::cout << i << std::endl;
std::cout << j << std::endl;
std::cout << k << std::endl;
std::cout << ding << std::endl;
}
void operator()()
{
std::cout << "callable object working" << std::endl;
}
};
void test(std::function<void(int, int, int)>& Func, int c)
{
Func(1, 2, 3);
std::cout << c<<std::endl;
}
int main()
{
std::function<void(int,int,int)>MemberCall=std::bind(&testclass::MemberFunc,testclass(), 1, std::placeholders::_1, std::placeholders::_2);
std::function<void(testclass&&,int, int, int)>MemberCall1 = std::bind(&testclass::MemberFunc, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,std::placeholders::_4);
MemberCall1(testclass(),1, 2, 3);
MemberCall(1,2,3);
std::function<void(testclass&, int, int, int) > MemberCall2=&testclass::MemberFunc;
testclass C;
MemberCall2(C, 1, 1, 1);
test(MemberCall, 3);
std::function<bool(int,int,int)> play=std::bind([&C](testclass& c, int i, int j, int k)->bool {
c.MemberFunc(i, j, k);
std::cout << "lambda working!" << std::endl;
}, C, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
play(1, 2, 3);
}
運行結果如下
相信細心的小伙伴已經發現我多加了一個Membercall1 并且使用了右值引用 關于這個 我們下一篇文章再說好嘛? 本期callable object講解結束 認真看完相信你能學到很多東西
原文鏈接:https://blog.csdn.net/qq_16401691/article/details/126417776
相關推薦
- 2022-06-07 python數組的復制與列表中的pop_python
- 2022-11-26 Mongodb?如何將時間戳轉換為年月日日期_MongoDB
- 2022-07-22 git倉庫的第一次上傳以及修改上傳項目
- 2022-03-12 C++類和對象之多態詳解_C 語言
- 2022-05-17 Spring boot 集成Redis客戶端Lettuce,導致服務線程數不斷增加
- 2022-04-10 el-input當type=number時只能輸入數字
- 2022-04-19 Python之random庫的常用函數有哪些_python
- 2022-05-26 Python?if?else條件語句形式詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支