假设我有3个类如下(因为这是一个例子,它不会编译!):
class Base
{
public:
Base(){}
virtual ~Base(){}
virtual void DoSomething() = 0;
virtual void DoSomethingElse() = 0;
};
class Derived1
{
public:
Derived1(){}
virtual ~Derived1(){}
virtual void DoSomething(){ ... }
virtual void DoSomethingElse(){ ... }
virtual void SpecialD1DoSomething{ ... }
};
class Derived2
{
public:
Derived2(){}
virtual ~Derived2(){}
virtual void DoSomething(){ ... }
virtual void DoSomethingElse(){ ... }
virtual void SpecialD2DoSomething{ ... }
};
我想创建Derived1或Derived2的实例,具体取决于在运行时之前不可用的某些设置。
由于我无法在运行时确定派生类型,那么您认为以下是不好的做法吗?...
class X
{
public:
....
void GetConfigurationValue()
{
....
// Get configuration setting, I need a "Derived1"
b = new Derived1();
// Now I want to call the special DoSomething for Derived1
(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}
private:
Base* b;
};
我一般都认为使用dynamic_cast很糟糕,但正如我所说,我不知道 在运行时创建的类型。请帮忙!
答案 0 :(得分:6)
为什么不通过将指向派生的指针分配给指向base的指针来延迟你“抛弃”某些东西的时刻:
void GetConfigurationValue()
{
// ...
// Get configuration setting, I need a "Derived1"
Derived1* d1 = new Derived1();
b = d1;
// Now I want to call the special DoSomething for Derived1
d1->SpecialD1DoSomething();
}
答案 1 :(得分:3)
虚函数的关键在于,一旦你拥有了正确类型的对象,就可以调用正确的函数而不用知道这个对象是哪个派生类 - 你只需调用虚函数,它做对了。
当你有一个派生类来定义不同的东西时,你只需要一个dynamic_cast
在基类中,你需要/想要和考虑额外的东西。
例如:
struct Base {
virtual void do_something() {}
};
struct Derived : Base {
virtual void do_something() {} // override dosomething
virtual void do_something_else() {} // add a new function
};
现在,如果您只想致电do_something()
,则dynamic_cast
完全没必要。例如,您可以拥有Base *
的集合,只需在每个集合上调用do_something()
,而无需关注对象是真的Base
还是Derived
当/如果您有Base *
,并且想要调用do_something_else()
,然后,您可以使用dynamic_cast
来确定对象本身实际上是Derived
,所以你可以调用它。
答案 2 :(得分:2)
使用dynamic_cast本身并不坏。不恰当地使用它是不好的做法,即不需要的地方。
以这种方式使用它也是一种不好的做法:
(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
原因:dynamic_cast(b)可能返回NULL。
使用dynamic_cast时,你必须格外小心,因为它不能保证,b实际上是Derived1类型而不是Derived2:
void GenericFunction(Base* p)
{
(dynamic_cast<Derived1*>(b))->SpecialD1DoSomething();
}
void InitiallyImplementedFunction()
{
Derived1 d1;
GenericFunction(&d1); // OK... But not for long.
// Especially, if implementation of GenericFunction is in another library
// with not source code available to even see its implementation
// -- just headers
}
void SomeOtherFunctionProbablyInAnotherUnitOfCompilation()
{
Derived2 d2;
GenericFunction(&d2); // oops!
}
您必须检查dynamic_cast是否确实成功。有两种方法:在演员表之前和之后检查它。在演员表演之前,您可以检查您尝试演绎的指针是否实际上是您期望通过RTTI进行的指示:
if (typeid(b) == typeid(Derived1*))
{
// in this case it's safe to call the function right
// away without additional checks
dynamic_cast<Derived1*>(b)->SpecialD1DoSomething();
}
else
{
// do something else, like try to cast to Derived2 and then call
// Derived2::SpecialD2DoSomething() in a similar fashion
}
事后检查它实际上有点简单:
Derived1* d1 = dynamic_cast<Derived1*>(b);
if (d1 != NULL)
{
d1->SpecialD1DoSomething();
}
我还说在C ++编程时尝试保存打字是一种不好的做法。 C ++中有许多功能似乎完全没有被打字更短(即让你感觉'在这里永远不会发生NULL'),但结果是后来调试的麻烦。 ;)
答案 3 :(得分:2)
您可能需要考虑的其他一些事项,以避免使用dynamic_cast
From Effective C ++(第三版) - 第35项虚拟功能的替代方案 -
答案 4 :(得分:1)
有什么问题:
Base * b;
if( some_condition ) {
b = new Derived1;
}
else {
b = new Derived2;
}
if ( Derived2 * d2 = dynamic_cast <Derived2 *>( b ) ) {
d2->SpecialD2DoSomething();
}
或者我错过了什么?
OI可以建议,在发布这样的问题时,你(以及其他人)会将你的A,B,C等类别和你的函数命名为f1(),f2()等等。它让人们的生活变得更轻松回答你的问题。
答案 5 :(得分:1)
有一个名为Factory Pattern的模式适合这种情况。这允许您根据某个输入参数返回正确类的实例。
享受!
答案 6 :(得分:1)
避免dynamic_cast
的一种方法是使用虚拟trampoline函数“SpecialDoSomething”,其派生的多态实现调用特定派生类的“SpecialDxDoSomething()”,它可以是您想要的任何非基类名。它甚至可以调用多个函数。