zoukankan      html  css  js  c++  java
  • 玩弄C++:回调一个类成员函数

    回调函数在C语言中用的非常频繁,它可以作用于结构体,但是到了C++中,类的成员函数是不能作为回调函数的。
    详细说来,C的结构体中的函数都是函数指针,调用的时候一般不操作结构体内的数据否则要显式地在调用时传入
    结构体的数据,即C中没有this这个概念。到了C++,假设类成员函数可以作为回调函数,那么这个函数对类成员
    变量的操作作用于哪个实例呢?这显然是无法确定的,只有主动告知之。
    关于this指针:看过C++类汇编代码的同志们都知道,在类实例调用它的成员函数时,call之前一定会有一步
      mov ecx, [ebp-xxx]
    这个指令就是根据栈基址找到改实例在栈中的地址,并放入ecx中;在被调用的函数中,所有对类成员变量的操作
    都是根据这个ecx带进来的地址完成的。因此,这就是类的成员函数不能作为回调函数的根本原因,那我们给他个
    ecx不就行了嘛~
     

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <stdarg.h>

    4. template </*return value*/typename RETYPE>
    5. class Function{
    6. public:
    7.     Function(){
    8.         this->objaddr = 0;
    9.         this->callback = 0;
    10.     }

    11.     Function(int *objAddr, void *cb){
    12.         objaddr = (int)objAddr;
    13.         callback = cb;
    14.     }

    15.     Function(Function &input){
    16.         this->objaddr = input.objaddr;
    17.         this->callback = input.callback;
    18.     }

    19.     static RETYPE call(int addr, void *callback, int n){
    20.         __asm{MOV ECX, addr}
    21.         return ((int (__stdcall *)(int)) callback)(n);
    22.     }

    23.     //need to be extended
    24.     static Function bind(void *objAddr, ...){
    25.         va_list stack;
    26.         va_start(stack, objAddr);

    27.         Function ret((int *)objAddr,
    28.             (void *)va_arg(stack, int));
    29.         return ret;
    30.     }

    31.     RETYPE operator()(int n){
    32.         int addr = objaddr;
    33.         void *cb = callback;
    34.         return call(addr, cb, n);
    35.     }
    36. private:
    37.     int objaddr;
    38.     void *callback;
    39. };


    40. class A{
    41. private:
    42.     Function<int> cb;
    43. public:
    44.     A(Function<int> callback)
    45.         :cb(callback)
    46.     {}
    47.     void t(int n){
    48.         cb(n);
    49.     }
    50. };

    51. class B{
    52. public:
    53.     int tt;
    54.     B():tt(0){
    55.         printf("B::tt inited as 0 ");
    56.     }
    57. //private:
    58.     int t(int n){
    59.         tt += n;
    60.         printf("B::tt changed to %d! ", tt);
    61.         return tt;
    62.     }
    63. };

    64. int main(){
    65.     B b1, b2;

    66.     A a(Function<int>::bind(&b1, B::t));
    67.     a.t(2);
    68.     a.t(3);
    69.     
    70.     Function<int> fx;
    71.     fx = Function<int>::bind(&b2, B::t);
    72.     fx(9);
    73.     return 0;
    74. }
     
    我定义了一个模板Function,它的作用有三点:
    1、把类成员函数的地址取出来,并转换成void *类型(通过进出栈来退化thiscall约定);见 bind;
    2、保存该回调函数作用的实例地址;
    3、提供调用该回调函数的方式(转换成stdcall,之前没有注意这点而被默认的cdecl搞得莫名其妙,因为它没有自动清栈);见 call。
    使用这个模板的方法很简单,它就是一个变量而已~
    记得跟我培训的C++大神说过,类成员函数是不能作为线程被创建的,原因应该也是这个;当然,用这个模板的话迂回一下也是可以实现的。
    阅读(439) | 评论(0) | 转发(1) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    Mac pycharm专业版安装以及破解方法
    Django 错误之 No module named ‘MySQLdb’
    archery部署
    MySQL监控内容
    mac安装神器brew
    4. 寻找两个有序数组的中位数
    7.整数反转
    2.两数相加
    1. 两数之和
    141. 环形链表
  • 原文地址:https://www.cnblogs.com/black/p/5171738.html
Copyright © 2011-2022 走看看