我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++11 成员函数作为回调函数的使用方式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++11 成员函数作为回调函数的使用方式

C++11成员函数作为回调函数使用

std::bind()被广泛地应用在新式的回调函数中。

C++11以前类的普通成员函数不能作为回调函数去注册,因为将普通成员函数注册给对方,但对方使用这个函数指针时,就会发生参数列表匹配的问题,因为少了隐含的this。

静态成员函数不包含this指针,所以一般将静态成员函数注册给对方。

C++11推出std::bind()和std::function搭配,前者生成新的调用对象,参数个数可以小于绑定函数的参数个数,少的参数,按位占用。后者保存函数调用类型的函数对象,使用该对象进行设置参数即可。

示例1

先看一个例子来热热身,熟悉一下std::bind和std::function

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

using namespace std;
// 使用std::bind时记得和::bind区别开,就怕作用于污染,误用::bind

//除法运算
class Division {
public:
    int operator()(int i, int j) { return i / j; }
};

//乘法运算
int Multiplication(int i, int j) { return i * j; }

//减法运算
int Substraction(int i, int j) { return i - j; }

//回调注册函数
int CallbackReg(function<int(int, int)> &func, int i, int j) { return func(i, j); }

//回调注册函数1,
int CallbackReq1(function<int(int)> &&func, int i) { return func(i); }

int main() {

    // 此function接受函数调用类型为int(int, int)的调用对象
    function<int(int, int)> func1 = [](int i, int j) { return i + j; }; //lambda
    function<int(int, int)> func2 = &Substraction;                        //函数指针
    function<int(int, int)> func3 = Multiplication;                        //函数名
    function<int(int, int)> func4 = Division();                            //重载调用运算符的对象

    //可将function类型存在容器中,来一次映射
    map<int, function<int(int, int)>> mpFuncs;
    mpFuncs[1] = func1;
    mpFuncs[2] = func2;
    mpFuncs[3] = func3;
    mpFuncs[4] = func4;

    //这里做着玩,映射一个数字和字符串
    map<int, string> mpOprs{{1, " + "}, {2, " - "}, {3, " * "}, {4, " / "}};

    // 便利map调用容器内函数对象们
    for (auto& it : mpFuncs) {
        cout << "calculator :" << 20 << mpOprs[it.first] << 5 
            << " = " << CallbackReg(it.second, 20, 5) << endl;
    }

    //使用std::bind,产生一个新的调用对象bindFunc(int i), 200作为int Multiplication(int i, 200)
    //std::placeholders 有个N个占位符(vs此版为20个):_N,表示占用绑定函数的第n个位子
    int pre = 300;
    auto bindFunc = std::bind(Multiplication, placeholders::_1, pre);
    cout << bindFunc(3) << endl;

    //bind最重要的一点在于参数绑定,如下例注册回调,参数就从2个变成了1个
    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之后来看一个平时常遇到的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; }

    //回调函数本数
    void MyCallBackFunc(string const& str) { cout << "The name of client is " << str << endl; }
    //注册回调,将this指针绑定到回调函数中
    void RegCallBackFunc() { if (!m_clt) return;  m_clt->SetCallBack(CallBackFuncType(std::bind(&Server::MyCallBackFunc, this, _1))); }
    //回调
    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

类成员函数作为回调函数的方法及注意点

编程中遇到一个错误,提示为error C2597: illegal reference to non-static member

即因为一个类的静态成员函数调用了类的非静态成员变量,而报错。

下面具体介绍一些相关知识点,以防下次再出错。

类成员函数当回调函数的方法

方法一:回调函数为普通的全局函数,但在函数体内执行类的成员函数

在创建线程调用回调函数时,传入类对象的指针(比如this指针)作为参数,并在回调函数中把void*强制转换为类的指针(MyClass*),就能使用该指针调用类的成员函数。

这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数。以外部函数作为回调函数,但执行的是成员函数的功能,这样相当于在中间作了一层转换。

缺点:回调函数在类外,影响了封装性。

方法二:回调函数为类内静态成员函数,在其内部调用类的非静态成员函数

此时需要一个指向类本身的、类的静态成员变量指针(static MyClass* CurMy),用来存储当前回调函数调用的对象,相当于法1中给回调函数传入的指针参数。在回调函数中通过CurMy指针调用类的成员函数。

优点:

  • 1、解决了法1的封装性问题
  • 2、没有占用callback的参数,可以从外界传递参数进来

缺点:每个对象启动子线程前一定要注意先让CurMy正确的指向自身,否则将为其它对象开启线程。

方法三:对成员函数进行强制转换,使其作为回调函数

这个方法是原理是,MyClass::func最终会转化成 void func(MyClass *this);即在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。

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

对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样,但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程是针对哪个对象建立的。

注意:此方法中FUNC函数的参数一定要是void*,这样才能在编译后把this指针转变为MyClass *this。

优点:法3的封装性比法2更好,因为不涉及多个对象共用一个静态成员的问题,每个对象可以独立地启动自己的线程而不影响其它对象。

为什么回调函数必须为静态函数?

普通的C++成员函数都隐含了一个“this”指针参数,当在类的非静态成员函数中访问类的非静态成员时,C++编译器通过传递一个指向对象本身的指针给其成员函数,从而能够访问类的数据成员。也就是说,即使你没有写上this指针,编译器在编译的时候自动加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。

正是由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数匹配失败。所以为了实现回调,类中的成员函数必须舍弃掉隐藏的this指针参数。因此,类中的回调函数必须为静态函数,加上static关键字。

类的静态成员函数如何访问非静态成员?

静态成员不属于某个具体的对象,而是被所有对象所共享。即静态成员属于整个类,不属于具体某个对象;非静态成员属于具体某个对象。因而静态成员函数只能访问类的静态成员,不能访问类中非静态成员。

那么,如何让静态函数访问类的非静态成员?

方法是:对于静态成员函数,我们显示的为其传递一个对象的首地址(该类的指针)。一般在这个静态成员函数的形参列表中加入一个  void*  类型的参数,来保存对象的首地址。并在该函数内部对该参数进行类型转换,通过类型转换后的参数来调用非静态成员。

或者用一个类的全局指针数组,保存每一个创建出来的类的this指针,用全局指针去调用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++11 成员函数作为回调函数的使用方式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++11 成员函数作为回调函数的使用方式

这篇文章主要介绍了C++11 成员函数作为回调函数的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

C++11lambda表达式在回调函数中的使用方式

这篇文章主要介绍了C++11lambda表达式在回调函数中的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

python中以函数作为参数(回调函数)的实现方法

这篇文章主要介绍了python中以函数作为参数(回调函数)的实现方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-01-04

c++中函数的调用方式

c++ 中有两种函数调用方式:值调用和引用调用。值调用传递参数副本,不影响原始变量;引用调用传递参数引用,修改引用会影响原始变量。根据函数目的和效率考虑,选择适当的调用方式:值调用保护原始变量,引用调用修改原始变量。C++ 中函数的调用方式
c++中函数的调用方式
2024-05-06

如何在 PHP 中使用函数作为回调?

在 php 中使用函数作为回调,可以使用匿名函数或命名函数。匿名函数语法为:$callback = function ($argument) { return $result; }。命名函数拥有名称,像常规函数一样定义。常用的内置函数接受回
如何在 PHP 中使用函数作为回调?
2024-04-10

C++ 函数参数传递方式与对象成员函数的关系

c++++ 函数参数可通过传值或传引用传递。对象成员函数的参数传递额外考虑 this 指针,默认情况下,this 指针作为隐式参数传递。非引用参数默认传值,引用参数隐式指向 this。C++ 函数参数传递方式与对象成员函数的关系在 C++
C++ 函数参数传递方式与对象成员函数的关系
2024-04-12

C++中的成员函数和友元函数怎么使用

成员函数是定义在类中的函数,它可以访问类中的成员变量和其他成员函数。成员函数使用类的对象来调用,并且隐式地访问该对象的成员。友元函数是在类外部定义的函数,它可以访问类的私有成员。友元函数可以在类中声明为友元,或者在类外部声明为友元。下面是成
2023-08-16

如何调用作为参数接收的 C 函数

编程网今天将给大家带来《如何调用作为参数接收的 C 函数》,感兴趣的朋友请继续看下去吧!以下内容将会涉及到等等知识点,如果你是正在学习Golang或者已经是大佬级别了,都非常欢迎也希望大家都能给我建议评论哈~希望能帮助到大家!问题内容我想使
如何调用作为参数接收的 C 函数
2024-04-04

C语言的回调函数怎么使用

本篇内容主要讲解“C语言的回调函数怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言的回调函数怎么使用”吧!对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级
2023-06-17

C语言的回调函数如何使用

这篇“C语言的回调函数如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C语言的回调函数如何使用”文章吧。1.程序架构一
2023-06-29

c语言中回调函数的使用方法及作用是什么

本篇内容介绍了“c语言中回调函数的使用方法及作用是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录前言一、通过这节课程你能掌握以下知识
2023-06-20

c++函数调用的方式有哪些

C++函数可以通过以下方式调用:1. 直接调用:使用函数名、参数列表和分号来调用函数。2. 函数指针调用:将函数的地址保存在指针变量中,然后通过指针变量调用函数。3. 函数引用调用:将函数的引用作为参数传递给另一个函数,并通过引用调用函数。
2023-08-18

C和C++的函数调用方式是什么

这篇文章主要介绍C和C++的函数调用方式是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!调用方式C/C++函数有多种调用约定。C语言:__cdecl__stdcall__fastcallnaked__pascal
2023-06-29

C++中有哪些函数调用的方式

这篇文章将为大家详细讲解有关C++中有哪些函数调用的方式,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录