本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
C++成员函数如何当作回调函数同时传递this指针
吴振华 · 231浏览 · 发布于2022-11-25 +关注

这篇文章主要介绍了C++成员函数如何当作回调函数同时传递this指针,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

就我目前了解所知,有三种函数可以作为回调函数:

  • 1.普通函数

  • 2.静态函数(我用得少没有写,直接跳过)

  • 3.成员函数

1.普通函数作为注册函数

普通函数作为回调函数,比较简单,只要函数签名(返回值类型+参数类型)一致就可以了。

因为普通函数不是类成员函数,如果想要访问类成员,在执行回调函数的时候,要把对象指针传给回调函数,如下代码:

namespace yy0
{
    //普通全局函数
    void call_back(void* pointer);
  
    class A
    {
    public:
        A() {
            //初始化指针
            p_call_back = NULL;
            //单数最大的数
            num = 9;
        }
        ~A() {}
    public:
        //打印这个数来验证是否正常调用回调函数
        int num;
    private:
        //指向回调函数的地址的指针
        void(*p_call_back)(void*);
  
    public:
        //用于注册回调函数
        void register_call_back(void(*p)(void*)) {
            if (p)
                p_call_back = p;
        }
  
        //执行回调函数
        void run_call_back() {
            if (p_call_back)
            {
                //把对象指针传递出去
                p_call_back(this);
            }      
        }
  
        //测试函数
        void test() {
            //注册
            register_call_back(call_back);
            //执行
            run_call_back();
        }
    };
  
    void call_back(void* pointer)
    {
        if (pointer)
        {
            //需要进行指针转换
            A* p = (A*)pointer;
            cout << "打印的值:" << p->num;
        }
    }
}
  
  
int main()
{
    yy0::A a;
    a.test();
    getchar();
    return 0;
}

结果正确打印,说明回调函数正常调用:

也可以定义一个全局的类对象指针:

namespace yy0
{
    //前置声明
    class A;
    //普通全局函数
    void call_back();
    //全局的类对象指针
    A* pointer = NULL;
    class A
    {
    public:
        A() {
            //给全局类对象指针赋值
            pointer = this;
            //初始化指针
            p_call_back = NULL;
            //单数最大的数
            num = 9;
        }
        ~A() {}
    public:
        //打印这个数来验证是否正常调用回调函数
        int num;
    private:
        //指向回调函数的地址的指针
        void(*p_call_back)();
      public:
        //用于注册回调函数
        void register_call_back(void(*p)()) {
            if (p)
                p_call_back = p;
        }
          //执行回调函数
        void run_call_back() {
            if (p_call_back)
            {
                //把对象指针传递出去
                p_call_back();
            }      
        }
          //测试函数
        void test() {
            //注册
            register_call_back(call_back);
            //执行
            run_call_back();
        }
    };
           void call_back()
    {
        if (pointer)
        {
            //需要进行指针转换
            A* p = (A*)pointer;
            cout << "打印的值:" << p->num;
        }
    }
}

这也可以正确执行,但是这种定义全局的对象指针有风险。如果只创建一个A的对象,就可以正常使用,不会出现什么太大问题。但是,一旦创建的对象个数≥2,那么就造成数据读取错误的问题。

可以想象一下,创建对象a1时,全局对象指针pointer是指向a1的位置,那么读取的pointer->num,是a1对象的num。

然后再创建a2,那么全局对象指针pointer就变成了指向a2的位置(因为pointer是个全局变量,从始至终只有一个这个变量),那么执行a2.text(),pointer->num读取的是a2的num。

如果执行a1.text(),那么此时,pointer->num读取的也是a2的num,而不是a1的num。更严重的是,一旦删除了a1或者a2,就会造成另外一个对象访问内存失败的问题。

2.静态函数作为注册函数

这个就自行上网查看吧,我用的少就不写了。

3.成员函数作为注册函数

假设场景:A类成员函数作为B类回调函数

《深度探索C++对象模型》这本书讲到,类成员函数都有一个隐藏参数用于传递this指针,这个this传递给函数由编译器来完成,不需要用户来做。

直接上代码:

namespace yy3
{
      class B
    {
    public:
        B() {
            pointer = NULL;
        }
        ~B() {}
      public:
        //存放A类的this指针
        void* pointer;
        //指向回调函数
        void(__stdcall *pCallBack)(void*);
    public:
        /*
        @函数作用:注册回调
        @输入参数:
        void(*p)(void*)         -- 输入A类的回调函数的地址
        void* p_this            -- 输入A类的this指针
        */
        //②
        void register_fun(void(__stdcall *p)(void*), void* p_this) {
            pCallBack = p;
            pointer = p_this;
        }
          //执行回调
        //③
        void run_call_back() {
            if (pCallBack)
                pCallBack(pointer);
        }
      };
        class A
    {
    public:
        A() {
            a = 5;
        }
        A(int num) {
            a = num;
        };
        ~A() {}
    public:
        //在A类中定义一个B类的变量
        B b;
        //拿来测试的变量
        double a;
          //定义联合,不知道原理,网上查到的技巧
        union for_callback {
            void(__stdcall *fun_int_c)(void*);
            void (A::*fun_in_class)(void*);
        }fp;
    public:
        //要拿来注册的回调函数
        void call_back(void* p) {
            A* pointer = (A*)p;
            //能打印出正确的a值就对了
            cout << "a:" << pointer->a << endl;
        }
          //测试函数
        //①
        void test() {
            fp.fun_in_class = &A::call_back;
            b.register_fun(fp.fun_int_c, this);
            b.run_call_back();
        }
      };
}
  int main()
{
    yy3::A a;
    a.test();
    getchar();
    return 0;
}

首先来解释一地方

1.__stdcall声明:这个看情况,我在公司电脑写的时候不需要加这个关键字,自己的电脑就要加这个。就是一个传参约定,可以上网查。

2.

//定义联合,不知道原理,网上查到的技巧
union for_callback {
    void(__stdcall *fun_int_c)(void*);
    void (A::*fun_in_class)(void*);
}fp;

使用union,这个说是为了逃避编译器检查,原理我也不太懂,如果有知道原理的大神,麻烦告诉一下下,感谢感谢。我直接就拿来用了。

3.前面说了成员函数有个隐含传递指针的参数,所以函数指针:

//指向回调函数
void(__stdcall *pCallBack)(void*);

需要定义参数为void*的函数指针,用于传递A类的this指针

4.因为函数指针是B类的成员,而函数指针接受的参数是A类的this指针,我们不能直接这样使用:

void run_call_back() {
    if (pCallBack)
        pCallBack(this);
}

这个pCallBack(this)中的this是指向B类对象的地址而非A类对象的地址,因此,在B类定义一个成员:void* pointr,用于保存A类对象的指针,然后这样使用

//执行回调
void run_call_back() {
    if (pCallBack)         
    pCallBack(pointer);
}

这样就运行回调函数,同时传递A类对象指针。

5.(无参这一点单独在这里说)当然,虽然成员函数有自带隐藏参数,我们也可以把它转换成无参的函数,修改这些地方:

//【1】
//指向回调函数
void(__stdcall *pCallBack)(void*);
//修改为
void(__stdcall *pCallBack)();
  //【2】
void register_fun(void(__stdcall *p)(void*), void* p_this) {
    pCallBack = p;
    pointer = p_this;
}
//修改为
void register_fun(void(__stdcall *p)(), void* p_this) {
    pCallBack = p;
    pointer = p_this;
}
  //【3】
//执行回调
void run_call_back() {
    if (pCallBack)
        pCallBack(pointer);
}
//修改为
void run_call_back() {
    if (pCallBack)
        pCallBack();
}
  //【4】
union for_callback {
    void(__stdcall *fun_int_c)(void*);
    void (A::*fun_in_class)(void*);
}fp;
//修改为
union for_callback {
    void(__stdcall *fun_int_c)();
    void (A::*fun_in_class)();
}fp;
  //【5】
//要拿来注册的回调函数修改为
void call_back() {
    cout << "a:" << this->a << endl;
}

这种情况编译能通过,但是void call_back()使用this指针,是无法正确读取内存的值,如下

言归正传。

成员函数转为带一个void*参数的函数运行情况如下:

 结果也是一个不正确的值,因此进行调试查看,把断点放在这个函数上,发现了一个奇怪的问题:

//要拿来注册的回调函数
void call_back(void* p)
{
    A* pointer = (A*)p;
    //能打印出正确的a值就对了
    cout << "a:" << pointer->a << endl;   
}

pointer是A类对象的指针,pointer通过函数指针pCallBack(pointr)传递给了call_back(void* p),从理论上讲,p的值要与pointer保持一致才对。但是p的值与pCaalBack相同,也就是p是函数指针,特别奇怪。我也不知道什么原因,所以如果有人知道,麻烦跟我讲一下,在这里先谢谢了。

我无法解决这个问题,所以尝试了将函数指针转为带有两个void*参数的函数,竟然可以传递正确的this指针,算是瞎猫碰上死耗子,代码跟上面类似,如下:

namespace yy3
{
      class B
    {
    public:
        B() {
            pointer = NULL;
        }
        ~B() {}
      public:
        //存放A类的this指针
        void* pointer;
        //指向回调函数
        void(__stdcall *pCallBack)(void*, void*);
    public:
        /*
        @函数作用:注册回调
        @输入参数:
        void(*p)(void*,void*)   -- 输入A类的回调函数的地址
        void* p_this            -- 输入A类的this指针
        */
        void register_fun(void(__stdcall *p)(void*, void*), void* p_this) {
            pCallBack = p;
            pointer = p_this;
        }
          //执行回调
        void run_call_back() {
            if (pCallBack)
                //需要两个指针作为参数,干脆就传递两个pointer吧
                pCallBack(pointer,pointer);
        }
      };
        class A
    {
    public:
        A() {
            a = 5;
        }
        A(int num) {
            a = num;
        };
        ~A() {}
    public:
        //在A类中定义一个B类的变量
        B b;
        //拿来测试的变量
        double a;
          //定义联合,不知道原理,网上查到的技巧
        union for_callback {
            void(__stdcall *fun_int_c)(void*, void*);
            void (A::*fun_in_class)(void*, void*);
        }fp;
    public:
        //要拿来注册的回调函数
        void call_back(void* p, void* pp)
        {
            A* pointer = (A*)p;
            //能打印出正确的a值就对了
            cout << "a:" << pointer->a << endl;   
        }
          //测试函数
        void test() {
            fp.fun_in_class = &A::call_back;
            b.register_fun(fp.fun_int_c, this);
            b.run_call_back();
        }
      };
}

结果是正确的:

 从图上可知,pCallBack(函数指针)的值,与p和pp都不同,无论是p还是pp,这两个值都是A类对象的地址,也就是说,已经成功把A的this指针传递进来了。因此结果也是正确的。

以上为个人经验,希望能给大家一个参考


c++

相关推荐

PHP实现部分字符隐藏

沙雕mars · 1312浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 896浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 957浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 953浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1076浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
坐标是江苏.南京,行业是互联网,技术是PHP和java,还有熟悉前后端等。
分类专栏
小鸟云服务器
扫码进入手机网页