欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++11?成員函數(shù)作為回調(diào)函數(shù)的使用方式

 更新時(shí)間:2022年11月05日 09:49:57   作者:歪鍋鍋  
這篇文章主要介紹了C++11?成員函數(shù)作為回調(diào)函數(shù)的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

C++11成員函數(shù)作為回調(diào)函數(shù)使用

std::bind()被廣泛地應(yīng)用在新式的回調(diào)函數(shù)中。

C++11以前類(lèi)的普通成員函數(shù)不能作為回調(diào)函數(shù)去注冊(cè),因?yàn)閷⑵胀ǔ蓡T函數(shù)注冊(cè)給對(duì)方,但對(duì)方使用這個(gè)函數(shù)指針時(shí),就會(huì)發(fā)生參數(shù)列表匹配的問(wèn)題,因?yàn)樯倭穗[含的this。

靜態(tài)成員函數(shù)不包含this指針,所以一般將靜態(tài)成員函數(shù)注冊(cè)給對(duì)方。

C++11推出std::bind()和std::function搭配,前者生成新的調(diào)用對(duì)象,參數(shù)個(gè)數(shù)可以小于綁定函數(shù)的參數(shù)個(gè)數(shù),少的參數(shù),按位占用。后者保存函數(shù)調(diào)用類(lèi)型的函數(shù)對(duì)象,使用該對(duì)象進(jìn)行設(shè)置參數(shù)即可。

示例1

先看一個(gè)例子來(lái)熱熱身,熟悉一下std::bind和std::function

#include <functional> //所需std::bind和std::function頭文件
#include <iostream>
#include <map>

using namespace std;
// 使用std::bind時(shí)記得和::bind區(qū)別開(kāi),就怕作用于污染,誤用::bind

//除法運(yùn)算
class Division {
public:
?? ?int operator()(int i, int j) { return i / j; }
};

//乘法運(yùn)算
int Multiplication(int i, int j) { return i * j; }

//減法運(yùn)算
int Substraction(int i, int j) { return i - j; }

//回調(diào)注冊(cè)函數(shù)
int CallbackReg(function<int(int, int)> &func, int i, int j) { return func(i, j); }

//回調(diào)注冊(cè)函數(shù)1,
int CallbackReq1(function<int(int)> &&func, int i) { return func(i); }

int main() {

?? ?// 此function接受函數(shù)調(diào)用類(lèi)型為int(int, int)的調(diào)用對(duì)象
?? ?function<int(int, int)> func1 = [](int i, int j) { return i + j; }; //lambda
?? ?function<int(int, int)> func2 = &Substraction;?? ??? ??? ??? ??? ??? ?//函數(shù)指針
?? ?function<int(int, int)> func3 = Multiplication;?? ??? ??? ??? ??? ??? ?//函數(shù)名
?? ?function<int(int, int)> func4 = Division();?? ??? ??? ??? ??? ??? ??? ?//重載調(diào)用運(yùn)算符的對(duì)象

?? ?//可將function類(lèi)型存在容器中,來(lái)一次映射
?? ?map<int, function<int(int, int)>> mpFuncs;
?? ?mpFuncs[1] = func1;
?? ?mpFuncs[2] = func2;
?? ?mpFuncs[3] = func3;
?? ?mpFuncs[4] = func4;

?? ?//這里做著玩,映射一個(gè)數(shù)字和字符串
?? ?map<int, string> mpOprs{{1, " + "}, {2, " - "}, {3, " * "}, {4, " / "}};

?? ?// 便利map調(diào)用容器內(nèi)函數(shù)對(duì)象們
?? ?for (auto& it : mpFuncs) {
?? ??? ?cout << "calculator :" << 20 << mpOprs[it.first] << 5?
?? ??? ??? ?<< " = " << CallbackReg(it.second, 20, 5) << endl;
?? ?}

?? ?//使用std::bind,產(chǎn)生一個(gè)新的調(diào)用對(duì)象bindFunc(int i), 200作為int Multiplication(int i, 200)
?? ?//std::placeholders 有個(gè)N個(gè)占位符(vs此版為20個(gè)):_N,表示占用綁定函數(shù)的第n個(gè)位子
?? ?int pre = 300;
?? ?auto bindFunc = std::bind(Multiplication, placeholders::_1, pre);
?? ?cout << bindFunc(3) << endl;

?? ?//bind最重要的一點(diǎn)在于參數(shù)綁定,如下例注冊(cè)回調(diào),參數(shù)就從2個(gè)變成了1個(gè)
?? ?cout << CallbackReq1(std::bind(Multiplication, placeholders::_1, 200), 2) << endl;

?? ?return 0;

}

calculator :20 + 5 = 25
calculator :20 - 5 = 15
calculator :20 * 5 = 100
calculator :20 / 5 = 4
900
400

好,在了解了std::bind和std::function之后來(lái)看一個(gè)平時(shí)常遇到的C++式的回調(diào)函數(shù)注冊(cè)

示例2

#include <functional>
#include <iostream>
#include <string>
#include <memory>

using namespace std;
using namespace std::placeholders; //占位符_N所在的命名空間

using CallBackFuncType = function<void(string const&)>;?

class Client {
public:
?? ?string name;
?? ?CallBackFuncType serverFunc;

?? ?Client() :name("Vergo"), serverFunc(nullptr) {}
?? ?~Client() {}

?? ?void SetCallBack(const CallBackFuncType &func) { serverFunc = func; }
?? ?void DoCallBack() { serverFunc(name); }
};

class Server {
public:
?? ?Client *m_clt;
?? ?Server() : m_clt(nullptr) { m_clt = new Client; }
?? ?~Server() { if (m_clt) delete m_clt; m_clt = nullptr; }

?? ?//回調(diào)函數(shù)本數(shù)
?? ?void MyCallBackFunc(string const& str) { cout << "The name of client is " << str << endl; }
?? ?//注冊(cè)回調(diào),將this指針綁定到回調(diào)函數(shù)中
?? ?void RegCallBackFunc() { if (!m_clt) return; ?m_clt->SetCallBack(CallBackFuncType(std::bind(&Server::MyCallBackFunc, this, _1))); }
?? ?//回調(diào)
?? ?void GiveMeCallBack() { if (!m_clt) return; m_clt->DoCallBack(); }
};


int main() {

?? ?Server testClass;
?? ?testClass.RegCallBackFunc();
?? ?testClass.GiveMeCallBack();?? ?

?? ?return 0;
}

The name of client is Vergo

類(lèi)成員函數(shù)作為回調(diào)函數(shù)的方法及注意點(diǎn)

編程中遇到一個(gè)錯(cuò)誤,提示為error C2597: illegal reference to non-static member

即因?yàn)橐粋€(gè)類(lèi)的靜態(tài)成員函數(shù)調(diào)用了類(lèi)的非靜態(tài)成員變量,而報(bào)錯(cuò)。

下面具體介紹一些相關(guān)知識(shí)點(diǎn),以防下次再出錯(cuò)。

類(lèi)成員函數(shù)當(dāng)回調(diào)函數(shù)的方法

方法一:回調(diào)函數(shù)為普通的全局函數(shù),但在函數(shù)體內(nèi)執(zhí)行類(lèi)的成員函數(shù)

在創(chuàng)建線程調(diào)用回調(diào)函數(shù)時(shí),傳入類(lèi)對(duì)象的指針(比如this指針)作為參數(shù),并在回調(diào)函數(shù)中把void*強(qiáng)制轉(zhuǎn)換為類(lèi)的指針(MyClass*),就能使用該指針調(diào)用類(lèi)的成員函數(shù)。

這樣做的原理是把當(dāng)前對(duì)象的指針當(dāng)作參數(shù)先交給一個(gè)外部函數(shù),再由外部函數(shù)調(diào)用類(lèi)成員函數(shù)。以外部函數(shù)作為回調(diào)函數(shù),但執(zhí)行的是成員函數(shù)的功能,這樣相當(dāng)于在中間作了一層轉(zhuǎn)換。

缺點(diǎn):回調(diào)函數(shù)在類(lèi)外,影響了封裝性。

方法二:回調(diào)函數(shù)為類(lèi)內(nèi)靜態(tài)成員函數(shù),在其內(nèi)部調(diào)用類(lèi)的非靜態(tài)成員函數(shù)

此時(shí)需要一個(gè)指向類(lèi)本身的、類(lèi)的靜態(tài)成員變量指針(static MyClass* CurMy),用來(lái)存儲(chǔ)當(dāng)前回調(diào)函數(shù)調(diào)用的對(duì)象,相當(dāng)于法1中給回調(diào)函數(shù)傳入的指針參數(shù)。在回調(diào)函數(shù)中通過(guò)CurMy指針調(diào)用類(lèi)的成員函數(shù)。

優(yōu)點(diǎn):

  • 1、解決了法1的封裝性問(wèn)題
  • 2、沒(méi)有占用callback的參數(shù),可以從外界傳遞參數(shù)進(jìn)來(lái)

缺點(diǎn):每個(gè)對(duì)象啟動(dòng)子線程前一定要注意先讓CurMy正確的指向自身,否則將為其它對(duì)象開(kāi)啟線程。

方法三:對(duì)成員函數(shù)進(jìn)行強(qiáng)制轉(zhuǎn)換,使其作為回調(diào)函數(shù)

這個(gè)方法是原理是,MyClass::func最終會(huì)轉(zhuǎn)化成 void func(MyClass *this);即在原第一個(gè)參數(shù)前插入指向?qū)ο蟊旧淼膖his指針。可以利用這個(gè)特性寫(xiě)一個(gè)非靜態(tài)類(lèi)成員方法來(lái)直接作為線程回調(diào)函數(shù)。

typedef void* (*FUNC)(void*);
FUNC callback = (FUNC)&MyClass::func;

對(duì)編譯器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)這兩種函數(shù)指針雖然看上去很不一樣,但他們的最終形式是相同的,因此就可以把成員函數(shù)指針強(qiáng)制轉(zhuǎn)換成普通函數(shù)的指針來(lái)當(dāng)作回調(diào)函數(shù)。在建立線程時(shí)要把當(dāng)前對(duì)象的指針this當(dāng)作參數(shù)傳給回調(diào)函數(shù)(成員函數(shù)func),這樣才能知道線程是針對(duì)哪個(gè)對(duì)象建立的。

注意:此方法中FUNC函數(shù)的參數(shù)一定要是void*,這樣才能在編譯后把this指針轉(zhuǎn)變?yōu)镸yClass *this。

優(yōu)點(diǎn):法3的封裝性比法2更好,因?yàn)椴簧婕岸鄠€(gè)對(duì)象共用一個(gè)靜態(tài)成員的問(wèn)題,每個(gè)對(duì)象可以獨(dú)立地啟動(dòng)自己的線程而不影響其它對(duì)象。

為什么回調(diào)函數(shù)必須為靜態(tài)函數(shù)?

普通的C++成員函數(shù)都隱含了一個(gè)“this”指針參數(shù),當(dāng)在類(lèi)的非靜態(tài)成員函數(shù)中訪問(wèn)類(lèi)的非靜態(tài)成員時(shí),C++編譯器通過(guò)傳遞一個(gè)指向?qū)ο蟊旧淼闹羔樈o其成員函數(shù),從而能夠訪問(wèn)類(lèi)的數(shù)據(jù)成員。也就是說(shuō),即使你沒(méi)有寫(xiě)上this指針,編譯器在編譯的時(shí)候自動(dòng)加上this的,它作為非靜態(tài)成員函數(shù)的隱含形參,對(duì)各成員的訪問(wèn)均通過(guò)this進(jìn)行。

正是由于this指針的作用,使得將一個(gè)CALLBACK型的成員函數(shù)作為回調(diào)函數(shù)時(shí)就會(huì)因?yàn)殡[含的this指針使得函數(shù)參數(shù)個(gè)數(shù)不匹配,從而導(dǎo)致回調(diào)函數(shù)匹配失敗。所以為了實(shí)現(xiàn)回調(diào),類(lèi)中的成員函數(shù)必須舍棄掉隱藏的this指針參數(shù)。因此,類(lèi)中的回調(diào)函數(shù)必須為靜態(tài)函數(shù),加上static關(guān)鍵字。

類(lèi)的靜態(tài)成員函數(shù)如何訪問(wèn)非靜態(tài)成員?

靜態(tài)成員不屬于某個(gè)具體的對(duì)象,而是被所有對(duì)象所共享。即靜態(tài)成員屬于整個(gè)類(lèi),不屬于具體某個(gè)對(duì)象;非靜態(tài)成員屬于具體某個(gè)對(duì)象。因而靜態(tài)成員函數(shù)只能訪問(wèn)類(lèi)的靜態(tài)成員,不能訪問(wèn)類(lèi)中非靜態(tài)成員。

那么,如何讓靜態(tài)函數(shù)訪問(wèn)類(lèi)的非靜態(tài)成員?

方法是:對(duì)于靜態(tài)成員函數(shù),我們顯示的為其傳遞一個(gè)對(duì)象的首地址(該類(lèi)的指針)。一般在這個(gè)靜態(tài)成員函數(shù)的形參列表中加入一個(gè)  void*  類(lèi)型的參數(shù),來(lái)保存對(duì)象的首地址。并在該函數(shù)內(nèi)部對(duì)該參數(shù)進(jìn)行類(lèi)型轉(zhuǎn)換,通過(guò)類(lèi)型轉(zhuǎn)換后的參數(shù)來(lái)調(diào)用非靜態(tài)成員。

或者用一個(gè)類(lèi)的全局指針數(shù)組,保存每一個(gè)創(chuàng)建出來(lái)的類(lèi)的this指針,用全局指針去調(diào)用。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語(yǔ)言入門(mén)篇--局部全局變量的作用域及生命周期

    C語(yǔ)言入門(mén)篇--局部全局變量的作用域及生命周期

    本篇文章是c語(yǔ)言基礎(chǔ)篇,本文對(duì)初識(shí)c語(yǔ)言的變量、局部全局變量的作用域及生命周期做了簡(jiǎn)要的概述,希望可以幫助大家快速入門(mén)c語(yǔ)言的世界,更好的理解c語(yǔ)言
    2021-08-08
  • 詳解C++中菱形繼承的原理與解決方法

    詳解C++中菱形繼承的原理與解決方法

    C++中的菱形繼承是多繼承的一種特殊情況,本文將通過(guò)海里帶大家了解一下菱形繼承形成的原因以及想應(yīng)的解決方法,感興趣的可以了解一下
    2023-02-02
  • C++線程中幾類(lèi)鎖的詳解

    C++線程中幾類(lèi)鎖的詳解

    這篇文章主要為大家介紹了C++線程中幾類(lèi)鎖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • C語(yǔ)言一篇精通鏈表的各種操作

    C語(yǔ)言一篇精通鏈表的各種操作

    鏈表是一種常見(jiàn)的重要的數(shù)據(jù)結(jié)構(gòu)。它是動(dòng)態(tài)地進(jìn)行存儲(chǔ)分配的一種結(jié)構(gòu),是根據(jù)需要開(kāi)辟內(nèi)存單元,鏈表這種數(shù)據(jù)結(jié)構(gòu),必須利用指針變量才能實(shí)現(xiàn),即一個(gè)結(jié)點(diǎn)中應(yīng)包含一個(gè)指針變量,用它存放下一結(jié)點(diǎn)的地址
    2022-04-04
  • 詳解樹(shù)形DP

    詳解樹(shù)形DP

    樹(shù)形DP是什么?跟其他DP有什么區(qū)別?相信很多初學(xué)者在剛剛接觸一種新思想的時(shí)候都會(huì)有這種問(wèn)題。沒(méi)錯(cuò),樹(shù)形DP準(zhǔn)確的說(shuō)是一種DP的思想,將DP建立在樹(shù)狀結(jié)構(gòu)的基礎(chǔ)上。所以我們結(jié)合具體題目進(jìn)行講解,希望大家可以在題目中領(lǐng)悟這種思想。
    2021-05-05
  • C語(yǔ)言淺析指針的使用

    C語(yǔ)言淺析指針的使用

    C語(yǔ)言這門(mén)課程在計(jì)算機(jī)的基礎(chǔ)教學(xué)中一直占有比較重要的地位,然而要想突破C語(yǔ)言的學(xué)習(xí),對(duì)指針的掌握是非常重要的,本文將具體針對(duì)指針的基礎(chǔ)做詳盡的介紹
    2022-07-07
  • c++實(shí)現(xiàn)簡(jiǎn)單的線程池

    c++實(shí)現(xiàn)簡(jiǎn)單的線程池

    本文介紹的線程池采用C++語(yǔ)言,在windows平臺(tái)下實(shí)現(xiàn)。本著技術(shù)分享的精神寫(xiě)作本文同時(shí)公布源代碼。歡迎大家指出該線程池存在的問(wèn)題并對(duì)當(dāng)前性能進(jìn)行討論。
    2015-03-03
  • 如何利用最簡(jiǎn)單的C語(yǔ)言實(shí)現(xiàn)AI五子棋

    如何利用最簡(jiǎn)單的C語(yǔ)言實(shí)現(xiàn)AI五子棋

    這篇文章主要給大家介紹了關(guān)于如何利用最簡(jiǎn)單的C語(yǔ)言實(shí)現(xiàn)AI五子棋的相關(guān)資料,包含了一些五子棋常見(jiàn)的功能,文中也通過(guò)詳細(xì)的實(shí)例代碼和圖片介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07
  • C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯(cuò)誤的問(wèn)題

    C++中關(guān)于std::queue?中遇到釋放內(nèi)存錯(cuò)誤的問(wèn)題

    這篇文章主要介紹了std::queue中遇到釋放內(nèi)存錯(cuò)誤的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • C語(yǔ)言復(fù)數(shù)的加減及輸出結(jié)構(gòu)體

    C語(yǔ)言復(fù)數(shù)的加減及輸出結(jié)構(gòu)體

    大家好,本篇文章主要講的是C語(yǔ)言復(fù)數(shù)的加減及輸出結(jié)構(gòu)體,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02

最新評(píng)論