dynamic_cast的继承和使用

时间:2010-06-17 20:02:45

标签: c++ inheritance oop dynamic-cast

假设我有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很糟糕,但正如我所说,我不知道 在运行时创建的类型。请帮忙!

7 个答案:

答案 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项虚拟功能的替代方案 -

  1. '模板方法模式'通过非虚拟接口(NVI)。使用公共方法“包装器”使虚拟函数成为私有/保护 - 允许您在虚拟方法之前和之后强制执行其他一些工作流程。
  2. '策略模式'通过函数指针。将额外方法作为函数指针传递。
  3. '策略模式'通过tr1 :: function。类似于2.但您可以提供具有各种选项的整个班级
  4. '策略模式'经典。来自主类的单独策略 - 将虚拟功能推送到另一个层次结构中。

答案 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()”,它可以是您想要的任何非基类名。它甚至可以调用多个函数。