从基类到不同派生类的多重继承转换
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从基类到不同派生类的多重继承转换相关的知识,希望对你有一定的参考价值。
我们假设有这样的类层次结构:
class A //base class
class B //interface
class C : public A, public B
然后创建C对象:
A *object = new C();
可以将对象转换为B吗?
重要提示:我假设我不知道该对象是C.我只知道它实现了接口B.
不可以。这是不可能的(从A*
直接铸造到B*
)。
因为A
和B
的地址位于class C
的不同位置。演员阵容总是不安全,可能会出现意想不到的行为。 Demo。
铸造应该总是通过class C
。例如
A* pa = new C();
B* pb = static_cast<C*>(pa);
^^^^ go through class C
从任何类型到任何其他类型的方式是dynamic_cast。但它要求对象具有多态性。通常,这需要将v表与A
和B
相关联,因此:如果A和B至少有一个虚函数,并且RTTI未禁用,
A* pa1 = new C;
A* pa2 = new A;
B* pb1 = dynamic_cast<B*>(pa1);
B* pb2 = dynamic_cast<B*>(pa2);
将导致pb2为null,并且pb1指向包含* pa1作为其A部分的对象的B部分。 (它是C或其他任何来自这两个碱基的事实并不重要)。
否则,在所有需要静态的情况下,你必须通过C
B* pb = static_cast<B*>(static_cast<C*>(pa));
请注意,static_cast<B*>(pA)
无法编译,因为A和B彼此无关。
是的,你应该首先将static_cast
对象转换为C *,然后你可以将static_cast
再次转换为B(虽然最后一次转换不需要,因为是标准转换)。我不确定static_cast
ing对象直接对B是否有效,请尝试查看是否出现编译错误。 reinterpret_cast
ing对象到B会让你遇到运行时崩溃,因为如果A和B都是非空的,它们会有不同的地址。
编辑更改问题后,将无法再执行所需操作。您需要知道继承树上下的正确路径,因为在具有多个继承和非空类的场景中进行转换意味着指针的移位。
只要对象派生自B,就可以始终将对象强制转换为B.当然,您只能调用接口B中定义的方法,因为虚拟指针只能访问虚拟表中B定义的方法。
当然可以。从逻辑上讲,如果您确定对象X是A的类型,那么这意味着您可以将其用作A.
实现这一目标的简单而天真的方法是使用标准C ++提供的dynamic_cast。然而,它将使用线性时间来查看vtable,因为dynamic_cast需要检查给定指针是否实际上可以被转换为给定类型,而不是像已经知道X是A类型的那些人。某些平台可能不提供RTTI ,让你可以做dynamic_cast。
还有另一个解决方案:让A和B中的一个知道它本身可能是一个超类,它正在进行多重继承。
#include <iostream>
#include <string>
struct Widget
{
virtual ~Widget() = default;
double widgetData;
};
struct DbItem
{
virtual ~DbItem() = default;
std::string nodeData;
};
struct GeneralItem
{
virtual ~GeneralItem() = default;
virtual void* cast(int type) = 0;
// virtual void const* cast(int type) const = 0; // Use this as well
// This is alternative for someone who don't like
// dynamic function!
void* ptrOfWidgetOrDbNode;
// If GeneralItem can know what it can be casted to.
// virtual Widget* toWidget() = 0;
// virtual DbItem* toDbItem() = 0;
};
enum { Widget_id, DbItem_id };
// Can be used as syntax candy.
Widget* toWidget(GeneralItem* gItem)
{
return static_cast<Widget*>(gItem->cast(Widget_id));
}
DbItem* toDbItem(GeneralItem* gItem)
{
return static_cast<DbItem*>(gItem->cast(DbItem_id));
}
struct TextView : Widget, GeneralItem
{
TextView() { widgetData = 20.0; }
void* cast(int type) override
{
switch ( type )
{
// WARNING: static_cast IS MANDATORY.
case Widget_id: return static_cast<Widget*>(this);
default: return nullptr;
}
}
};
struct ImageView : GeneralItem, Widget
{
ImageView() { widgetData = 40.0; }
void* cast(int type) override
{
switch ( type )
{
// WARNING: static_cast IS MANDATORY.
case Widget_id: return static_cast<Widget*>(this);
default: return nullptr;
}
}
};
struct SoundData : DbItem, GeneralItem
{
SoundData() { nodeData = "Sound"; }
void* cast(int type) override
{
switch ( type )
{
// WARNING: static_cast IS MANDATORY.
case DbItem_id: return static_cast<DbItem*>(this);
default: return nullptr;
}
}
};
struct VideoData : GeneralItem, DbItem
{
VideoData() { nodeData = "Video"; }
void* cast(int type) override
{
switch ( type )
{
// WARNING: static_cast IS MANDATORY.
case DbItem_id: return static_cast<DbItem*>(this);
default: return nullptr;
}
}
};
GeneralItem* getDbItem();
GeneralItem* getWidget();
int main()
{
{
// This is definitely subclass of Widget, but
// GeneralItem has no relationship with Widget!
GeneralItem* gItem = getWidget();
Widget* nowWidget = static_cast<Widget*>(gItem->cast(Widget_id));
std::cout << nowWidget->widgetData << std::endl;
delete gItem;
}
{
// This is definitely DbItem!
GeneralItem* gItem = getDbItem();
// DbItem* nowDbItem = static_cast<DbItem*>(gItem->cast(DbItem_id));
// You can use sugar!
DbItem* nowDbItem = toDbItem(gItem);
std::cout << nowDbItem->nodeData << std::endl;
delete gItem;
}
}
GeneralItem* getDbItem()
{
return new VideoData;
}
GeneralItem* getWidget()
{
return new TextView;
}
在此示例代码中,有3个基类,2个getter函数和一些子类。 Widget和DbItem是您无法触及的类,而GeneralItem旨在成为执行多重继承的类的超类。
getDbItem() : GeneralItem*
是一个返回GeneralItem的函数,它也是DbItem的实例。 getWidget() : GeneralItem*
是类似的,返回实例的类型必须是Widget。
GeneralItem有一个虚函数cast(type_id) : void*
,它返回给定类型的指针,但是在void指针形式中,因为返回类型是在编译时确定的。它可以通过static_cast或reinterpret_cast转换回你想要的类型:任何一个都可以使用。
你需要注意cast()
实现中超类和static_cast的任意顺序:如果你错过了static_cast,你的void指针将被映射到子类,而不是精确的DbItem
或Widget
。省略static_cast将允许你干净,即使没有任何警告编译结果,因为将类型指针强制转换为void指针是完全合理的。
这只是各种选择中的一种解决方案,因此如果您正在寻找可以在不同情况下使用的解决方案,请告诉我。
我的英语不太好,上下文中可能会有一些语法错误。
以上是关于从基类到不同派生类的多重继承转换的主要内容,如果未能解决你的问题,请参考以下文章