• 我擦C++ 反人类啊


    多继承二义性什么的反人类啊啊啊!!回调函数什么的也反人类啊!!

    作为一名C# 程序猿表示实现个什么接口之类的挺好的,到C++ 就不一样了啊。事件委托什么的也很好用啊,到C++ 又不一样了啊!!!

    好的~ 那么今天就来吐槽一下回调函数吧~

    本来想实现个类似于C# 里面事件的东西来做类之间的通信,然后发现了凶残的回调函数,在cocos2d 里面是用的menu_selector 之类的东西,然后传进去个函数指针引用。。。嗯。。看起来很吊的样子,于是照葫芦画瓢写了一个,里面包含了个函数指针类型的成员。嗯,大致类似是这样:

     1 //=================stdafx.h
     2 #pragma once
     3 
     4 #include "targetver.h"
     5 
     6 #include <stdio.h>
     7 #include <tchar.h>
     8 
     9 #include "OBJ.h"
    10 
    11 typedef void (OBJ::*XXFUNC) (void);
    12 #define XXFUNC_SELECTOR(_p) (XXFUNC)(&_p)
    13 
    14 //=================B.h
    15 #pragma once
    16 #include "stdafx.h"
    17 class B :
    18     public OBJ
    19 {
    20 private:
    21     XXFUNC func;22 
    23 public:
    24     void AddListener(XXFUNC func);
    25     void TriggerEvent();
    26     B(void);
    27     ~B(void);
    28 };

    这里的B 就相当于被观察者,负责触发事件给其添加的listener 函数。看起来是不是很像委托(C#)的用法..嗯..

    然后来个观察者A:

     1 #pragma once
     2 
     3 #include "OBJ.h"
     4 #include "B.h"
     5 
     6 class A : public OBJ
     7 {
     8 private:
     9     int m_xxxx;
    10     B* m_b;
    11     void callback();
    12 
    13 public:
    14     A(void);
    15     ~A(void);
    16     void Print(void);
    17     void AnotherPrint(void);
    18 };

    看起来没什么问题,哦对了,OBJ 是个啥都没有的基类,貌似为了使那个函数指针类型能过作为类的成员,定义指针类型的时候还像这样加了个OBJ::

    1 typedef void (OBJ::*XXFUNC) (void);

    好了,然后是类的实现了:

     1 #include "StdAfx.h"
     2 #include "B.h"
     3 
     4 B::B(void)
     5 {
     6     this->func = NULL;
     7 }
     8 
     9 
    10 B::~B(void)
    11 {
    12 }
    13 
    14 void B::AddListener(XXFUNC func)
    15 {
    16     this->func = func;
    17 } 18 19 20 void B::TriggerEvent() 21 { 22 if (this->func) 23 { 24 (this->*func)(); 25 } 26 }

    嗯。。。看上去没什么问题,然后是A:

     1 #include "StdAfx.h"
     2 #include "A.h"
     3 #include "B.h"
     4 
     5 
     6 A::A(void)
     7 {
     8     this->m_xxxx = 0;
     9     this->m_b = new B();
    10     this->m_b->AddListener(XXFUNC_SELECTOR(A::callback));
    11 }
    12 
    13 
    14 A::~A(void)
    15 {
    16 }
    17 
    18 void A::callback()
    19 {
    20     this->Print();
    21     this->m_xxxx = 200000;
    22     this->Print();
    23 }
    24 
    25 void A::Print()
    26 {
    27     printf("%d
    ", this->m_xxxx);
    28 }
    29 
    30 void A::AnotherPrint()
    31 {
    32     this->m_b->TriggerEvent();
    33 }

    嗯,看上去也没啥问题,回调的时候输出两次自己的m_xxxx 的值嘛。。为了省事直接就内部实例化B,然后注册回调函数,对外有个AnotherPrint 让B 触发下事件,好吧可能这样设计有问题,不过不要在意这些细节,只是让监听者去触发了事件。。。。懒得改了,就这样吧。。

    然后是main:

    1 int _tmain(int argc, _TCHAR* argv[])
    2 {
    3     A a;
    4     a.Print();
    5     a.AnotherPrint();
    6     return 0;
    7 }

    嗯,和刚才理由一样,懒得管B,所以就这样了。。

    好了,运行看看效果吧,先后调用了Print 和AnotherPrint,也就是说,A实例化之后先输出了一次m_xxxx,然后触发了B 的事件,由于A 监听了这个事件,所以A 执行了callback,又调用了Print,修改了m_xxxx之后再调用次Print,理想输出应该是:

    0
    0
    200000

    好的,编译运行:

    0
    17830428
    200000
    请按任意键继续. . .

    嗯。。。。不错,出来三个数,等等,中间那个是个啥?!?!这么有节奏的一串数字怎么混进去的!!一定是刚刚运行的方式不对,再试一下:

    0
    19534364
    200000
    请按任意键继续. . .

     (╯°Д°)╯︵ ┻━┻ 你tm 在逗我!!

    好(wo)~吧(cao)~看来只能调试了呢,然后发现了重大问题。。回调函数里的this 不是this 了啊!!!地址跟main 里面实例化的那个A 的地址不一样啊,你到底指的哪里啊亲,居然还能给成员赋值成功再打印出来啊!!你什么时候出现的啊!!!

    这不科学啊!! 坑爹呢这是(对于C# 程序猿来说,在C# 的事件回调里的this 就代表上层作用域的那个类的那个实例)!!!

    然后问了下同学,同学说“成员函数是没有被分配空间的啊魂淡,你不能这样玩的”。。。 听不懂啊完全不知道在说什么啊但是又好像很厉害的样子啊怎么办。

    然后上网查了一下,找到了这样:http://www.cnblogs.com/this-543273659/archive/2011/08/17/2143576.html,还有这样:http://baike.baidu.com/link?url=P1TXY3WyaOT98mVsvTRHaZSZWcCIJsMPxpxOiFyXEhAgI4uUt8EkZ2C_tYWasUqn 的东西,哦~ 原来非静态成员函数含有个默认参数this,原来如此~ (喂,真的懂了么,怎么感觉没懂的样子) ,但是这个默认的this 到底在回调函数被调用的时候变成啥了啊!! 居然还能给成员赋值成功再打印出来啊!!你什么时候出现的啊!!!

    好(wo)~吧(cao)~你(wan'er)赢(wo)了(ne)~ 于是我给那个函数指针类型加了个参数... 像这样:

    1 typedef void (OBJ::*XXFUNC) (OBJ*);
    2 #define XXFUNC_SELECTOR(_p) (XXFUNC)(&_p)

    再把B 里面加上这俩东西:

    private:
        OBJ* handler;
    
    public:
        void AddListener(OBJ* handler, XXFUNC func);

    嗯,没错,addlistener 多了个OBJ* 的参数。。。 居然叫handler!!哪里来的勇气!!!

    然后A 的回调变成了这样:

    1 void A::callback(A* a)
    2 {
    3     a->Print();
    4     a->m_xxxx = 200000;
    5     a->Print();
    6 }

    好的~编译运行吧:

    0
    0
    200000
    请按任意键继续. . .

    哈哈哈哈哈哈哈哈哈哈,居然成功了。。。 太不好玩了。。。。谁能告诉我那个this 到底指哪去了?!!居然还能给成员赋值成功再打印出来啊!!你什么时候出现的啊!!!

    =================Edited on 01/12/2014======================

    擦泪,今天又看了下cocos2d 里面传这种回调的实现,原来cocos2d 里面类似于我这个的东西确实在addListener 这种函数里面除了回调当作参数还真有传进去个OBJ,就像我的那个从

    void AddListener(XXFUNC func);

    变成

    void AddListener(OBJ* handler, XXFUNC func);

    一样,最重要的是在被观察者的调用这个指针的时候出现的不一样,我是这样调用的:

    void B::TriggerEvent()
    {
        if (this->func)
        {
            (this->*func)();
        }
    }

    这里的this 是B 的那个this,应该是B* const 类型,但是cocos2d 里面是类似这种方法调用的:

    void B::TriggerEvent()
    {
        if (this->func && this->handler)
        {
            (this->handler->*func)();
        }
    }

    没(diao)有(bao)错(le)!这个handler 是addListener 的时候加进去的那个,这样的话!我们就不需要把函数指针里面再加上那个OBJ* 的参数了!! 就可以直接用this 了!!!this 就正常了!!!!!嗯,我验证下。。。

    哈哈哈哈哈哈哈哈哈哈果然是对的!!!

    那么接下来的问题就是如果不用 this->handler->*func(); 来调用而使用this->*func(); 的时候在callback 里面的this 到底是啥,于是又debug 了下:

    A:            0x0045fa50

    B:            0x00296458

    'this'      in A::callback:   0x00296458   // 我擦果然是B !!!!

    'this->m_b'  in A::callback:   0x0045fa50   // 我擦把B 当成A 之后里面的m_b (B* 类型)居然指向的是A !!!! 什么乱起八灶的!!你指的这么乱你家里人知道么!!! (╯°Д°)╯︵ ┻━┻

    嘛。。最后据说写blog 用到的代码都要传到github 上才叫nb。。,那么为了早日nb(好像哪里不对):

    https://github.com/strawhatboy/CppTuTsau_1

  • 相关阅读:
    去除文本多余空行
    自定义裁剪图片
    遍历文件目录下所有图片并保存到统一路径
    根据节点解析xml
    坐标转换——GCJ-02
    获取进程列表
    判断进程状态
    VSDK modify HDMI resolution
    mcspi
    TI RTOS
  • 原文地址:https://www.cnblogs.com/strawhatboy/p/3515003.html
Copyright © 2020-2023  润新知