我遇到了一个非常有趣的问题,我希望你能帮我解决我在这里做错的事情。
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!!!
希望你能告诉我这里的错误。谢谢
答案 0 :(得分:5)
答案和评论并没有完全归结到底部,所以我会尝试解决这个问题。
首先看一下,如图所示使用void*
强制转换应该不会有问题,因为你最终会向基础投射,对吧?因此,即使在某些时候通过void*
丢失了该类型的信息,它应该被重新获得?虚函数调用应该仍然有效吗?
如果没有多重继承,他们会。在单继承场景中,仍将调用正确的虚函数。但是当你有多个继承时,你需要知道类型来正确计算其中一个基数的偏移量。
一点背景知识。虚函数通常通过所谓的 VTABLE 表示 - 可以将其视为指向函数的指针表。对于每个特定的类,都有一个这样的表的副本(每个类,而不是每个对象!),它将指针设置为在该类中定义的相应成员函数。该类的每个对象都有一个指向该表的指针(以便同一个类的多个对象共享同一个表)。
对于单继承,只有一个vtable指针,它是该类的第一个成员。因此,将指针放在两者之间并不重要,只要在调用虚函数之前将其转换为正确的类型,就会调用正确的虚函数。
但是,在多个非虚拟继承的情况下,vtable有多个ponter。每个父母都有一个指针!因此,当您转换为父级时,结果指针将指向其中一个基础 - 通过将指向对象的指针偏移到指向您所投射的类的vtable的指针。如果您首次投放到void*
,则不执行此关键步骤,结果void*
只是指向您班级中的第一个vtable。即使你回到适当的基础,也不再需要信息来执行适当的偏移 - 所有编译器看到的都是void*
,它没有其他信息 - 并且指针仍然指向第一个vtable
由于这一切,你最终会从错误的基础调用虚函数!