我有以下问题, 让我们假设用户使用RightParent项目, 我还需要添加功能,我在LeftParent,
左右父功能中的一些是纯虚拟
传递给User的实际项目继承自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();
}
问题是, 当我持有该项目,并从左侧或右侧父项调用方法时,它们被正确调用,
当我把item指定为(指向)rightParent(因为它可以从Qt小部件获得)时,我知道它是从两者继承的一些CItem(CItem1,CITem2 ...)(所以CItem是CLeft_Parent也是CItem是CRight_Parent),当我尝试将它强制转换为leftParent时,对leftParent方法的调用无法正常工作(在调试器中,跳转到vftable的某处就好像它被破坏了一样)
任何想法?
答案 0 :(得分:1)
多重继承是非常强大的东西。
然而,使用铸造时需要特别小心。不幸的是,你所做的是未定义的行为。
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将是一个更安全的替代方案。
用于说明可能的铸件的小图片: