我有以下问题,
假设用户使用RightParent项目,
我还需要在LeftParent中具有的其他功能,
左右父级的某些功能是纯虚拟的
传递给用户的实际项目继承自rightParent(原始合同)和leftParent(我在此处添加的合同)
它可能看起来像这样(可编译,并且在VS 2010中也是如此)
左父母-新增合同/功能(即我拥有该课程)
#pragma once
struct CParent_Left
{
CParent_Left(){}
virtual void Foo() const =0;
virtual ~CParent_Left(){}
};
正确的父母-原始合同(实际上是Qt对象-即我不拥有该类)
#pragma once
struct CParent_Right
{
CParent_Right(){}
virtual void Bar() const =0;
virtual ~CParent_Right(){}
};
项(存在多个项,并且程序中使用的所有项均从父母双方继承)
#pragma once
#include "CParent_Left.h"
#include "CParent_Right.h"
class CItem : public CParent_Left, public CParent_Right
{
public:
CItem(){}
virtual void Foo() const override {}
virtual void Bar() const override {}
virtual ~CItem(){}
};
用户(通常是从Qt QObject继承的用户)本身(和Qt私有内幕)将项目视为rightParent,此处实现的附加功能有时需要将项目视为leftParent
#pragma once
#include "CParent_Left.h"
struct CParent_Right;
class CUser
{
CParent_Right * data; // normally obtained from parent Qt class
public:
CUser(CParent_Right * data) : data(data){}
void CallFoo()
{
((CParent_Left*)data)->Foo(); // calls Bar()
}
virtual ~CUser(){}
};
样本主
#include "CItem.h"
#include "CUser.h"
void main(int argc, char *argv[])
{
auto item(new CItem());
item->Foo(); // calls foo
auto user(new CUser(item));
user->CallFoo();
}
问题是,
当我拿着该项目并从左父项或右父项调用方法时,它们会被正确调用,
当我将项目保持为rightParent的指针(可以从Qt小部件获得)时,我知道它是从这两者继承的一些CItem(CItem1,CITem2 ...)(因此CItem为CLeft_Parent,CItem为CRight_Parent ),当我尝试将其强制转换为leftParent时,对leftParent方法的调用无法正常工作(在调试器中,跳转到vftable的位置好像已损坏)
有任何想法吗?
最佳答案
多重继承确实非常强大。
但是,使用铸件时需要格外小心。不幸的是,您所做的是不确定的行为。
这是怎么了?
首先,通过向构造函数传递CUser
指针来创建CItem
。
由于仅为CParent_Right
定义了构造函数,因此编译器会将CItem
强制转换为CParent_Right
。
从这一刻开始,传递给构造函数的指针就是CParent_Right
子对象的指针。您不能再假定它是CItem
了;并非所有CParent_Right
子对象都一定是CItems
!
因此,强制转换((CParent_Left*)data)->Foo();
不会像您期望的那样产生指向CParent_Left
的指针。它仅采用CParent_Right
的当前地址,并将其用作CParent_Left
所在的地址。这是未定义的行为。对于您的编译器,它只采用vtable中第一个函数的地址,这会导致这种不匹配。
如何纠正呢?
如果确定构造函数的CParent_Right实际上是CItem,则允许您向下转换到CItem。这是合法的并且可以正确完成。然后,您可以将CItem转换为CParent_Left,它将按预期工作:
// ((CParent_Left*)data)->Foo(); // calls Bar() ===> OUCH !!!!
static_cast<CParent_Left*>(static_cast<CItem*>(data))->Foo(); // YES !!
请注意,我在这里使用static_cast是因为我确定我的对象类型,并且即使对于非多态类也可以使用这种构造。但是,如果有疑问,dynamic_cast将是一个更安全的选择。
对象布局和投射
一张小图来说明可能的转换:
关于c++ - parent __vftable之间的多重继承转换似乎已损坏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31090190/