2015-09-25 24 views
0

我有以下代碼。在轉換爲基類後訪問派生類成員時崩潰

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中)我觀察了一些崩潰。

在我們處理回調函數時,我發現函數'DoJob'被調用,'MyDrivedClass'的'this'指針無效,我無法訪問'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的正確解決方案。

+0

<<在調試期間,我發現在創建'ForCallback'對象時,我們傳遞'MyDrivedClass'的地址,但傳遞給'ForCallback'構造函數的地址不同。 >>這怎麼會發生?它只是傳遞指針,地址不會被改變。 – Balu

+0

你的代碼不能編譯。事實上,它甚至不是任何可編譯代碼的簡化版本。請讓您的示例正確編譯,或者使用[this](http://pastebin.com/0mmpfDLJ)代替。 – stgatilov

+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++標準會對此發表評論。

相關問題