2016-01-26 105 views
1

I`ve面臨着一個非常有趣的問題,我希望你能幫助我什麼,我做錯了..C++調用函數導致調用另一個函數

class abstract1 
{ 
public: 
    virtual ~ abstract1(){}; 
    virtual void funk1()=0; 
    virtual void punk1()=0; 
}; 
class abstract2 
{ 
public: 
    virtual ~ abstract2(){}; 
    virtual void funk2()=0; 
    virtual void punk2()=0; 
}; 

class Derived: public abstract1, 
       public abstract2 
{ 
public: 
    Derived(){ cout<<"Derived constructor"<<endl;}; 
    ~Derived() {cout <<"Derived destructor" <<endl;}; 
    void funk1(){ 
    cout<<"funk1 function in Derived!!!"<<endl; 
    }; 
    void punk1(){ 
    cout<<"punk1 in Derived!!!"<<endl; 
    }; 
    void funk2(){ 
     cout<<"funk2 function in Derived!!!"<<endl; 
    }; 
    void punk2(){ 
     cout<<"punk2 in Derived!!!"<<endl; 
    }; 

}; 

class myapi{ 
public: 
    void start(void *_drved){ 
    drved=(abstract2*)_drved; 
    }; 
    void callback(){ 
    drved->funk2(); 
    drved->punk2(); 
    } 
protected: 
    abstract2* drs; 
}; 

在這裏,我定義了兩個基類和從這兩個繼承的派生類。 main()的實現如下:

int main() { 

    Derived* myderived =new Derived(); 
    myapi* dbmodule= new myapi(); 
    dbmodule->start(myderived); 
    dbmodule->callback(); 
    return 0 
    } 

我期望看到funk2和punk2越來越稱爲此起彼伏。然而結果對我來說是新的。 Callback()似乎稱爲funk1和punk1。屏幕輸出是象下面這樣:

Derived constructor 
funk1 function in Derived!!! 
punk1 in Derived!!! 

希望你可以讓我知道我得到錯誤here.Thanks

+2

對void指針使用'(abstract2 *)'結束於'reinterpret_cast'而不是'dynamic_cast'。 – user1810087

+0

比較'std :: cout << reinterpret_cast (myderived)'和'std :: cout << static_cast (myderived)' – molbdnilo

+2

不要使用'void *'use'Derived *'或者,因爲你最終使用指向'abstract2'的指針,從一個指向該指針的指針開始,這將允許您使用任何派生自該類的類,而不會產生void *類型的不安全。 [將void *替換爲Derived *會產生您期望的行爲](http://coliru.stacked-crooked.com/a/a98279daeb5c57ef) – jaggedSpire

回答

5

的答案和意見不太再往它的底部,所以我會嘗試解決這個問題。

在第一次看,使用void*強制轉換不應該是一個問題,因爲你最終鑄造到基地,對吧?所以即使在某種程度上這種類型的信息通過void*丟失了,它應該被重新使用?虛函數調用應該仍然有效?

他們會,如果不是多重繼承。在單繼承場景中,正確的虛函數仍然會被調用。但是當你有多重繼承時,你需要知道這個類型來正確計算其中一個基數的偏移量。

有點背景。虛擬功能通常通過所謂的VTABLE來表示 - 可以被認爲是指向功能的指針表。對於每個特定的類都有這樣的表的副本(每,而不是每個對象!),它將指針設置爲該類中定義的相應成員函數。該類的每個對象都有一個指向該表的指針(以便同一個類的多個對象都共享同一個表)。

對於單繼承,只有一個vtable指針,它是類的第一個成員。因此,只要在調用虛擬函數之前將其轉換爲正確類型,就不用擔心如何在兩者之間投射指針,您將調用正確的虛函數。

但是,在多個非虛擬繼承的情況下,vtable有多個ponter。你有一個指針給每個家長!因此,當你投射到父項時,生成的指針將指向其中一個基址 - 通過將指向該對象的指針偏移指向您轉換爲的類的vtable的指針。如果你第一次投到void*,這個關鍵步驟不會被執行,並且導致void*只是指向你班級中的第一個vtable。甚至當你退回到適當的基地時,不再需要信息來執行適當的抵銷 - 編譯器看到的所有信息都是void*,它不會有任何信息 - 指針仍然指向第一個vtable。

由於這一切,你最終會從不正確的基地調用虛擬功能!