代码之家  ›  专栏  ›  技术社区  ›  Silverspur

C++多态性通过C回调丢失

  •  0
  • Silverspur  · 技术社区  · 7 年前

    registerCallback

    • 用于注册回调的C风格函数指针
    • void* “user\u data”传输任何用户认为合适的数据

    第一个参数,我写了这个函数:

    extern C {
        void handler( void* userData ) {
            (static_cast<Base*>(userData))->handle();
        }
    }
    

    功能如下:

    class Base {
        public void handle() {}
    };
    ...
    Base b;
    registerCallback( handler, &b );
    

    现在,让我们添加一点复杂性:

    Base 类具有纯虚函数 toto handle() 我有 Child1 Child2 扩展的类 基础 .

    class Base {
        public:
            void handle() { toto(); }
            virtual void toto() = 0;
    };
    class Child1: public Base {
        public: virtual void toto() { cout << "CHILD 1" << endl; }
    };
    class Child2: public Base {
        public: virtual void toto() { cout << "CHILD 2" << endl; }
    };
    ...
    Child1 c;
    registerCallback( handler, &c ); // --> seg fault!!
    

    userData Base* 托托 在里面 句柄() Base::toto() 方法,触发回调时会导致seg故障。

    另一方面,如果我不投 基础* ‘void*’ is not a pointer-to-object type

    我怎样才能避免通过回调来剥离变量的实际类型?

    以下是来自GDB的堆栈跟踪:

    Stacktrace:
    #0  0x0000000000000000 in ?? ()
    #1  0x00007ffff7bce41d in Base::handle ...
    #2  0x00007ffff7bce110 in handler ...
    

    handler

    (gdb) p (static_cast<Base*>(userData))->handle
    Cannot take address of method handle
    
    2 回复  |  直到 7 年前
        1
  •  6
  •   Brian Bi    7 年前

    因为在extern-C函数中,我 userData Base* ,调用 toto 在里面 handle() Base::toto() 方法

    这不应该是个问题。因为 Base

    Child1* 直接发送到 void* 当你经过时 &c registerCallback 无效* 值将指向 Child1 对象当该指针转换为 基础* 在里面 handler ,结果值也将指向 儿童1 基础 的子对象 儿童1

    为了确保你拥有后者,你需要 &c 之前 . 这确保指针值将被调整为指向 儿童1 对象然后指针将来回穿过 无效*

    (这是对评论中指出的内容的补充,你可能忘记了 儿童1 Child2 派生自

        2
  •  3
  •   Ben Voigt    7 年前

    可以将派生对象指针隐式转换为基指针。仅仅因为你不需要写任何代码,并不意味着没有转换。

    static_cast 从…起 void* ,将永远不会有任何调整。指针最初必须来自精确的(模 )指针类型。只有相关类型是不够的。

    struct Base {};
    struct Derived : Base {};
    
    Derived d;
    Base* pb = &d; // ok
    void* pv = &d;  // legal but wrong
    Base* pb2 = reinterpret_cast<Base*>(pv);  // legal but wrong
    

    在这个序列之后, pb2 Base 的子对象 d ,它指向 d 无效* 您稍后将检索到:

    Derived d;
    Base* pb = &d; // ok
    void* pv = pd;  // pointer to subobject
    Base* pb2 = reinterpret_cast<Base*>(pv);  // correct!
    

    在您的代码中,这意味着您必须强制执行 &c Base* 通话前 registerCallback .