让我们假设有这样的类层次结构:
class A //base class
class B //interface
class C : public A, public B
然后创建C对象:
A *object = new C();
是否可以将对象转换为B?
重要:我假设我不知道该对象是C.我只知道它实现了接口B
答案 0 :(得分:11)
否即可。这是不可能的(从A*
直接投射到B*
)。
因为A
和B
的地址位于class C
的不同位置。所以演员阵容总是不安全,可能会出现意外行为。 Demo
施法应始终通过class C
。 e.g。
A* pa = new C();
B* pb = static_cast<C*>(pa);
^^^^ go through class C
答案 1 :(得分:8)
从任何类型转到任何其他类型的方式是 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彼此不相关。
答案 2 :(得分:3)
是的,您应首先static_cast
反对C *,然后您可以再次static_cast
到B(虽然不需要最后一次投射,因为是标准转换)。我不确定static_cast
对象直接对B是否有效,请尝试查看是否出现编译错误。对{B} reinterpret_cast
对象会导致运行时崩溃,因为如果它们都非空,则A和B会有不同的地址。
修改更改问题后,您无法再执行所需操作。您需要知道继承树上下的正确路径,因为在具有多个继承和非空类的场景中进行转换意味着指针的移位。
答案 3 :(得分:0)
只要对象派生自B,就可以随时将对象转换为B.当然,您只能调用接口B中定义的方法,因为虚拟指针只能访问B中定义的方法。虚拟表。
答案 4 :(得分:0)
当然可以。从逻辑上讲,如果您确定对象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*
,该虚函数返回给定类型的指针,但以空指针形式返回,因为返回类型是在编译时确定的。可以通过static_cast或reinterpret_cast将其转换回所需的类型:可以使用任何一种。
您需要注意cast()
实现中父类和static_cast的任意顺序:如果错过了static_cast,则您的void指针将映射到子类,而不是精确的DbItem
或{{1} }。省略static_cast将使您保持干净,即使没有任何警告编译结果,因为将类型化的指针强制转换为void指针是完全合理的。
这只是各种选择中的一种解决方案,因此,如果您正在寻找可以在不同情况下使用的解决方案,请告诉我。
我的英语不太好,在上下文中可能存在一些语法错误。