C++ 类层次结构中动态_cast的替代方案
我有一个类结构,不同子类的负载都继承自同一个抽象基类。这个基类之所以存在,是因为所有这些类都在编译时自注册到构建它们的工厂。这真的很好,让我不必在下游某处维护一个巨大的开关或任何其他流动机制C++ 类层次结构中动态_cast的替代方案,c++,inheritance,dynamic-cast,virtual-functions,C++,Inheritance,Dynamic Cast,Virtual Functions,我有一个类结构,不同子类的负载都继承自同一个抽象基类。这个基类之所以存在,是因为所有这些类都在编译时自注册到构建它们的工厂。这真的很好,让我不必在下游某处维护一个巨大的开关或任何其他流动机制 +---------------+ | Base | +--+---------+--+ | | +-------+-+ +-+-
+---------------+
| Base |
+--+---------+--+
| |
+-------+-+ +-+-------+
| Kid1 | | Kid2 |
+----+----+ +----+----+
| |
+---+---+---+ +---+---+---+
| | | | ... | | | | ....
但是,这些Kid1
和Kid2
类是不同的,问题是我必须在代码中的某个地方区分它们,我唯一拥有的是Base
指针。我不想用这件事来打扰工厂,并且尽可能简单
我现在解决这个问题的方法是让Base
对这两个兄弟姐妹有所了解。它有一个虚拟方法type()
,它返回一个区别于Kid1
和Kid2
的枚举类型。两个孩子都忽略了这一点(基本上说我是KidX),这样我就知道我在和谁打交道。然后使用此类型将dynamic_cast
Base
播送给两个孩子中的任何一个,然后程序继续进行
然而,这是正确的方法吗
例如,我可以添加更多的virtual
方法作为基础,一方面只使用层次结构的一部分使用的方法来污染它,另一方面为我节省dynamic\u cast
有什么想法吗?您不需要
dynamic\u cast
,dynamic\u cast
已经使用内部运行时类型信息来确定它是否能够将传递的指针/引用强制转换为所需的类型,但您已经通过type()
方法对其进行了检查。在您的情况下,static\u cast
就足够了,因为您已经自己提供了RTTI
或者,您也可以删除type()方法,直接使用dynamic_cast
,如果无法将对象强制转换为所需类型,则会生成nullptr
如果你真的想避免这样的事情,那么你必须将你想要的行为封装到
虚拟
方法中,这样无论你在哪里需要使用Kid1
或Kid2
,你都会有不同的实现,而不需要真正区分两者,只需让多态性发挥作用。在多重继承中,dynamic_cast将根据需要移动指针,这是必须的。因此,摆脱它是危险的。如果您的系统扩展到某个地方使用MI,您可能会出现各种奇怪的行为。也许您可以使用typeid
操作符。因为:
N3337 5.2.8/2规定:
当typeid应用于类型为多态类类型的glvalue表达式(10.3)时,结果引用
指向std::type_info对象,该对象表示最派生对象(1.8)的类型(即动态
glvalue引用的类型
例如:
struct Base
{
virtual void f(){} //to ensure that Base is polymorphic object
};
struct Derived: Base{};
struct AnotherDerived: Base{};
int main()
{
Base *ptr = new AnotherDerived;
if(typeid(*ptr) == typeid(Derived)) //Do not forget to dereference pointer, otherwise typeid() will return type_info for pointer itself
{
cout << "ptr is pointing to object of type Derived" << endl;
}
if (typeid(*ptr) == typeid(AnotherDerived))
{
cout << "ptr is pointing to object of type AnotherDerived" << endl;
}
}
struct Base
{
virtual void f(){}//以确保Base是多态对象
};
派生结构:基{};
另一个派生的结构:Base{};
int main()
{
Base*ptr=新的另一个派生的;
如果(typeid(*ptr)=typeid(派生))//不要忘记取消引用指针,否则typeid()将返回指针本身的type_信息
{
cout我通常更喜欢在不使用强制转换的情况下获得所需类型的句柄。这意味着向基类添加方法:
virtual Kid1 *asKid1()
{
return null;
}
virtual Kid2 *asKid2()
{
return null;
}
然后,子类会根据需要覆盖这些:
// (in Kid1)
virtual Kid1 *asKid1() override
{
return this;
}
尽管您的Base
类仍然被额外的方法“污染”,但即使子类在将来被扩展,也只需要这两个方法
(您可以让它们返回引用而不是指针,在这种情况下,默认情况下它们应该引发异常)。您可以尝试
您的类型()的替代品
成员只需尝试对Kid1进行动态转换,然后Kid2…绑架,看看哪一个成功。不漂亮。只是指出这是一个替代方案。@JesperJuhl我也曾玩弄过这个想法,但不能忽略丑陋的静态转换
,当向上移动和向上移动时,隐式转换也会正确地移动指针拥有类层次结构,包括多重继承。如果没有dynamic\u cast
@CemKalyoncu,交叉转换将无法工作,因为它是“不安全的”在编译器不允许虚拟继承的情况下。这通常意味着在实践中使用虚拟继承是完全安全的;否则会出现编译时错误。@CemKalyoncu:请不要将ideone用于代码段。尽管他们的服务条款称这些代码段将“永远”托管,他们还是会删除这些代码段。该示例显示了一个没有给出编译器错误但代码正确运行的情况。也就是说,它不仅限于MI。它用于使用静态\u强制转换进行向下转换。@BenVoigt Ok,您推荐哪个站点?谢谢您的回答。您的建议基本上意味着50%的强制转换将失败。这不是很不可取吗?您说过需要在代码中区分它们,这也是不可忽视的,因为它会阻止多态性发挥作用。这是一个XY问题。首先解释为什么以及如何区分它们,以便我们能够提供合适的解决方案。感谢您的输入。我基本上是用额外的知识来做这件事的。我可以按照@Jack的建议去做简单地说“试试”@Montaldo当您需要区分它们时,为什么要使用指向基的指针?自注册结构需要一个通用的基类型,以便从工厂中构建。这一方面是一种幸事,但代价是这样的。仔细阅读OP的问题可以揭示为什么这个答案不可行。有一个基类Base
,两个子类Kid1
和Kid2
,它们还有更多的子类