class 层次结构中 dynamic_cast 的替代项
Alternatives to dynamic_cast in class hierarchy
我有一个 class 结构,其中包含大量不同的 child class es,它们都继承自相同的抽象基础 class。这个基础 class 在那里是因为所有这些 classes 在编译时自注册到它们被构建的工厂。这真的很整洁,让我免于在下游某处维护一个巨大的开关或任何其他流机制的负担。
+---------------+
| Base |
+--+---------+--+
| |
+-------+-+ +-+-------+
| Kid1 | | Kid2 |
+----+----+ +----+----+
| |
+---+---+---+ +---+---+---+
| | | | ... | | | | ....
但是,这些 Kid1
和 Kid2
class 是不同的,问题是我必须在我的代码中的某处区分它们,而我唯一拥有的是Base
指针。我不想为此打扰工厂并尽可能保持简单。
我现在解决这个问题的方法是让 Base
稍微了解这两个兄弟姐妹。它有一个虚拟方法 type()
,其中 returns 是一个区分 Kid1
和 Kid2
的枚举类型。两个孩子都忽略了这个 (基本上说我是 KidX) 这样我就知道我在和谁打交道。然后将此类型用于 dynamic_cast
Base
给其中一个孩子,程序继续进行。
但是,这是正确的方法吗?
例如,我可以向基础添加更多 virtual
方法,一方面用层次结构的一部分使用的方法污染它,但另一方面节省了 dynamic_cast
。
有什么想法吗?
您不需要 dynamic_cast
,dynamic_cast
已经使用内部运行时类型信息来确定它是否能够将传递的 pointer/reference 转换为所需的类型,但您已经在检查它通过 type()
方法。 static_cast
在您的情况下就足够了,因为您已经自己提供了 RTTI。
或者您也可以删除 type() 方法并直接使用 dynamic_cast
,如果它无法将对象转换为您想要的类型,则会产生 nullptr
。
如果你真的想避免这样的事情,那么你必须将所需的行为封装到 virtual
方法中,这样无论你需要使用 Kid1
或 Kid2
,你将有不同的实现而无需真正区分两者,只需让多态性发挥作用即可。
在多重继承中,dynamic_cast会根据需要移动指针,这是必须的。因此,摆脱它是危险的。如果您的系统扩展到使用 MI 的某个点,您可能会遇到各种奇怪的行为。
也许您可以使用 typeid
运算符。因为:
N3337 5.2.8/2 说:
When typeid is applied to a glvalue expression whose type is a polymorphic class type (10.3), the result refers
to a std::type_info object representing the type of the most derived object (1.8) (that is, the dynamic
type) to which the glvalue refers ....
示例:
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;
}
}
但是,如果您必须在代码中区分它们,为什么要使用指向 Base 的指针?如何将仅使用 Base
属性的代码与使用 child 类 的特定属性的代码分开?这就是多态性的全部意义。
编辑:
正如 davmac 指出的那样。只有当这些 sub类 最派生 类.
时,它才会起作用
我通常更喜欢能够在不使用强制转换的情况下获得所需类型的句柄。这意味着将方法添加到 Base class:
virtual Kid1 *asKid1()
{
return null;
}
virtual Kid2 *asKid2()
{
return null;
}
子classes 然后适当地覆盖这些:
// (in Kid1)
virtual Kid1 *asKid1() override
{
return this;
}
虽然你的Base
class仍然"polluted"有额外的方法,但只需要这两个方法,即使subclasses在未来。
(你可以让它们 return 引用而不是指针,在这种情况下它们应该默认抛出异常)。
您可以尝试 Visitor pattern。
When should I use the Visitor Design Pattern?
我有一个 class 结构,其中包含大量不同的 child class es,它们都继承自相同的抽象基础 class。这个基础 class 在那里是因为所有这些 classes 在编译时自注册到它们被构建的工厂。这真的很整洁,让我免于在下游某处维护一个巨大的开关或任何其他流机制的负担。
+---------------+
| Base |
+--+---------+--+
| |
+-------+-+ +-+-------+
| Kid1 | | Kid2 |
+----+----+ +----+----+
| |
+---+---+---+ +---+---+---+
| | | | ... | | | | ....
但是,这些 Kid1
和 Kid2
class 是不同的,问题是我必须在我的代码中的某处区分它们,而我唯一拥有的是Base
指针。我不想为此打扰工厂并尽可能保持简单。
我现在解决这个问题的方法是让 Base
稍微了解这两个兄弟姐妹。它有一个虚拟方法 type()
,其中 returns 是一个区分 Kid1
和 Kid2
的枚举类型。两个孩子都忽略了这个 (基本上说我是 KidX) 这样我就知道我在和谁打交道。然后将此类型用于 dynamic_cast
Base
给其中一个孩子,程序继续进行。
但是,这是正确的方法吗?
例如,我可以向基础添加更多 virtual
方法,一方面用层次结构的一部分使用的方法污染它,但另一方面节省了 dynamic_cast
。
有什么想法吗?
您不需要 dynamic_cast
,dynamic_cast
已经使用内部运行时类型信息来确定它是否能够将传递的 pointer/reference 转换为所需的类型,但您已经在检查它通过 type()
方法。 static_cast
在您的情况下就足够了,因为您已经自己提供了 RTTI。
或者您也可以删除 type() 方法并直接使用 dynamic_cast
,如果它无法将对象转换为您想要的类型,则会产生 nullptr
。
如果你真的想避免这样的事情,那么你必须将所需的行为封装到 virtual
方法中,这样无论你需要使用 Kid1
或 Kid2
,你将有不同的实现而无需真正区分两者,只需让多态性发挥作用即可。
在多重继承中,dynamic_cast会根据需要移动指针,这是必须的。因此,摆脱它是危险的。如果您的系统扩展到使用 MI 的某个点,您可能会遇到各种奇怪的行为。
也许您可以使用 typeid
运算符。因为:
N3337 5.2.8/2 说:
When typeid is applied to a glvalue expression whose type is a polymorphic class type (10.3), the result refers to a std::type_info object representing the type of the most derived object (1.8) (that is, the dynamic type) to which the glvalue refers ....
示例:
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;
}
}
但是,如果您必须在代码中区分它们,为什么要使用指向 Base 的指针?如何将仅使用 Base
属性的代码与使用 child 类 的特定属性的代码分开?这就是多态性的全部意义。
编辑: 正如 davmac 指出的那样。只有当这些 sub类 最派生 类.
时,它才会起作用我通常更喜欢能够在不使用强制转换的情况下获得所需类型的句柄。这意味着将方法添加到 Base class:
virtual Kid1 *asKid1()
{
return null;
}
virtual Kid2 *asKid2()
{
return null;
}
子classes 然后适当地覆盖这些:
// (in Kid1)
virtual Kid1 *asKid1() override
{
return this;
}
虽然你的Base
class仍然"polluted"有额外的方法,但只需要这两个方法,即使subclasses在未来。
(你可以让它们 return 引用而不是指针,在这种情况下它们应该默认抛出异常)。
您可以尝试 Visitor pattern。
When should I use the Visitor Design Pattern?