传承与dynamic_cast的用法(Inheritence and usage of dynami

2019-11-03 20:36发布

假设我有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的用法是不好的,但正如我所说,我不知道要创建哪种类型,直到运行时间。 请帮忙!

Answer 1:

使用的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将永远不会发生在这里”),但练得屁股调试事后疼痛。 ;)



Answer 2:

为什么不耽误上,你通过指定一个指向派生的指针基地“扔掉”了一些,如果类型信息的那一刻:

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();
}


Answer 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()在每一个,无需支付任何关注对象是否真的是一个BaseDerived

当/如果你有一个Base * ,并且要调用do_something_else() 那么你可以使用一个dynamic_cast找出对象本身是否真的是一个Derived ,所以你可以调用。



Answer 4:

你可能会喜欢一些其他的事情要考虑,以避免使用中的dynamic_cast

从有效的C ++(第三版) - 项目35个替代虚函数 -

  1. 经由非Vitual接口(IIL)“模板方法模式”。 使得虚函数私有/与公共法“包装”保护 - 让您执行的东西,其他一些工作流程之前和虚拟方法后做。
  2. “策略模式”经由函数指针。 在通过额外的方法作为函数指针。
  3. “策略模式”通过TR1 ::功能。 类似于2,但你能提供整个班级,不同的选项
  4. “策略模式”的经典。 从主类独立的战略 - 推动虚函数到另一个层次。


Answer 5:

有一个名为模式工厂模式 ,将适合这个场景。 这使您可以返回基于一些输入参数的正确类的一个实例。

请享用!



Answer 6:

出什么问题了:

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()等,使生活更轻松的人回答你的问题。



Answer 7:

避免的方法之一dynamic_cast是有一个虚拟的蹦床功能“SpecialDoSomething”其派生态实现调用特定的派生类的“SpecialDxDoSomething()”,它可以是你想用的任何非基本类名。 它甚至可以调用多个功能。



文章来源: Inheritence and usage of dynamic_cast