2016-01-26 90 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。

由于这一切,你最终会从不正确的基地调用虚拟功能!