假设我有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的用法是不好的,但正如我所说,我不知道要创建哪种类型,直到运行时间。 请帮忙!
使用的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将永远不会发生在这里”),但练得屁股调试事后疼痛。 ;)
为什么不耽误上,你通过指定一个指向派生的指针基地“扔掉”了一些,如果类型信息的那一刻:
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();
}
虚函数的一点是,一旦你有正确的对象,就可以调用正确的函数不知道它的派生类这个对象是-你只需要调用虚函数,并做正确的事。
你只需要一个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
,所以你可以调用。
你可能会喜欢一些其他的事情要考虑,以避免使用中的dynamic_cast
从有效的C ++(第三版) - 项目35个替代虚函数 -
- 经由非Vitual接口(IIL)“模板方法模式”。 使得虚函数私有/与公共法“包装”保护 - 让您执行的东西,其他一些工作流程之前和虚拟方法后做。
- “策略模式”经由函数指针。 在通过额外的方法作为函数指针。
- “策略模式”通过TR1 ::功能。 类似于2,但你能提供整个班级,不同的选项
- “策略模式”的经典。 从主类独立的战略 - 推动虚函数到另一个层次。
有一个名为模式工厂模式 ,将适合这个场景。 这使您可以返回基于一些输入参数的正确类的一个实例。
请享用!
出什么问题了:
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()等,使生活更轻松的人回答你的问题。
避免的方法之一dynamic_cast
是有一个虚拟的蹦床功能“SpecialDoSomething”其派生态实现调用特定的派生类的“SpecialDxDoSomething()”,它可以是你想用的任何非基本类名。 它甚至可以调用多个功能。