转换为baseclass后访问派生类成员时崩溃

时间:2015-09-25 07:29:44

标签: c++ visual-studio-2015

我有以下代码。

class BaseHandler
{
    //Blank class
};
typedef void    (BaseHandler::*InstantFunc)();

class MyDrivedClass: public BaseHandler
{

    //Constructor
    ...
    // Data Members
    ...
    char *m_someMember;
    ...
    void DoJob();
    //More functions
    ...

}
MyDrivedClass::DoJob()
{
    //accessing Work Something with 'm_someMember' data;
}


class ForCallback
{
public:
    ForCallback(BaseHandler* handler, InstantFunc callback);

    BaseHandler* GetHandler();
    InstantFunc         GetCallback();

    void                Handle(SProgressiveInfo info);

protected:
    BaseHandler*     m_handler;
    InstantFunc      m_callback;
};

ForCallback::ForCallback(BaseHandler* handler, InstantFunc callback):
m_handler(handler), m_callback(callback)
{

}

 BaseHandler* ForCallback::GetHandler()
{
    return m_handler;
}

InstantFunc ForCallback::GetCallback()
{
    return m_callback;
}

void ForCallback::Handle()
{
    (m_handler->*(m_callback))();
}

//thread1--
somefunction()
{
    //Creating mydrivedclass.
    MyDrivedClass *drived = new MyDrivedClass();


    ForCallback *cBack = new ForCallback(drived, MyDrivedClass::DoJob);

    //store cback in some global map
    StoreInGlobalMap(cBack);


}


//Thread2---
someotherfunction
{
    //Get cback from the global map

    ForCallback *cBack = GetFromMap();
    cback->Handle();
}

当我在iOS中运行此代码(使用xcode编译)时它工作正常,但是当我在Windows上运行此代码(使用visual studio编译)时,我观察到一些崩溃。

在调查我在处理回调时在thread2中找到的函数' DoJob'被叫,这个'这个' ' MyDrivedClass'的指针无效,我无法访问' m_someMember'。我正在使用互斥锁来同步线程之间的数据,并且没有同步问题。

在调试过程中,我发现在创建' ForCallback'的对象时我们正在传递“MyDrivedClass”的地址。但地址传递给' ForCallback'的构造函数。是不同的。这可能是由于派生类转换为基类指针

我找到的解决方法是将ForCallback的定义更改为

ForCallback::ForCallback(void* handler, InstantFunc callback):
m_handler(()BaseHandler*)handler), m_callback(callback)
{

}

但这仍然是一种解决方法。

请有人解释iOS(Xcode编译)和Windows(Visual Studio 2015编译)上不同行为的原因 什么是适用于iOS和Windows的正确解决方案。

1 个答案:

答案 0 :(得分:0)

派生对象的地址可能不同,因为它具有虚方法,而基类则没有。有关说明,请参阅this question。此外,如果使用虚拟继承,则地址也不同。此外,如果使用多重继承,则基类的地址可能不同。 在构造函数中将MyDrivedClass*转换为BaseHandler*时,编译器足够聪明,可以适当地更改地址。

但是,成员函数指针强制转换并不是那么清楚。您可以阅读this article以了解它们有多复杂。实际上,您不能简单地将MyDrivedClass的成员函数指针指向BaseHandler的成员函数指针,而MSVC2013会告诉您:

temp.cpp(34) : error C2664: 'ForCallback::ForCallback(const ForCallback &)' : cannot convert argument 2 from 'void (__cdecl MyDrivedClass::* )(void)' to 'InstantFunc'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

另一方面,您可以使用static_cast或C风格强制转换它。在这种情况下,MSVC2013会警告您:

temp.cpp(34) : warning C4407: cast between different pointer to member representations, compiler may generate incorrect code

我认为在这种情况下调用类型为BaseHandler的对象的方法是不正确的,因为类BaseHandler没有这样的方法。所以你在这里称之为不存在的方法。我希望有人能够更好地了解C ++标准。