当您发现自己想要在基类中使用像“IsConcreteX”这样的成员函数(编辑:或更准确地说,像“ConcreteX *GetConcreteX”这样的函数)时,您基本上是在实现自己的dynamic_cast。例如:
class Movie
{
// ...
virtual bool IsActionMovie() const = 0;
};
class ActionMovie : public Movie
{
// ...
virtual bool IsActionMovie() const { return true; }
};
class ComedyMovie : public Movie
{
// ...
virtual bool IsActionMovie() const { return false; }
};
void f(Movie const &movie)
{
if (movie.IsActionMovie())
{
// ...
}
}
这可能看起来比dynamic_cast 更干净,但仔细观察,您很快就会意识到除了“邪恶”dynamic_cast 不再出现在您的代码中(提供你没有使用一个没有实现dynamic_cast的古老编译器!:))。更糟糕的是——“自行编写的动态转换”方法冗长、容易出错且重复,而 dynamic_cast 无需在类定义中添加任何代码即可正常工作。
所以真正的问题应该是在某些情况下基类知道具体派生类是否有意义。答案是:通常不会,但你肯定会遇到这种情况。
用非常抽象的术语来考虑软件的一个组件,它将对象从一个部分 (A) 传输到另一部分 (B)。这些对象的类型为Class1 或Class2,Class2 is-a Class1。
Class1
^
|
|
Class2
A - - - - - - - -> B
(objects)
但是,B 仅对Class2 有一些特殊处理。 B 可能是系统的完全不同的部分,由不同的人编写,或者是遗留代码。在这种情况下,您希望在不进行任何修改的情况下重用 A 到 B 通信,并且您也可能无法修改 B。因此,明确询问您是在处理另一端的 Class1 还是 Class2 对象可能是有意义的。
void receiveDataInB(Class1 &object)
{
normalHandlingForClass1AndAnySubclass(object);
if (typeid(object) == typeid(Class2))
{
additionalSpecialHandlingForClass2(dynamic_cast<Class2 &>(object));
}
}
这是一个不使用typeid的替代版本:
void receiveDataInB(Class1 &object)
{
normalHandlingForClass1AndAnySubclass(object);
Class2 *ptr = dynamic_cast<Class2 *>(&object);
if (ptr != 0)
{
additionalSpecialHandlingForClass2(*ptr);
}
}
如果Class2 不是叶类(即,如果可能有进一步从它派生的类),这可能更可取。
最后,这通常归结为您是从一开始就设计一个包含所有部分的整个系统,还是必须在后期修改或调整其中的一部分。但是,如果您发现自己遇到了上述问题,您可能会喜欢 dynamic_cast 作为在正确情况下正确工作的正确工具。