代码之家  ›  专栏  ›  技术社区  ›  barak manos

使用dynamic_cast代替传统多态性有什么好处?

  •  6
  • barak manos  · 技术社区  · 12 年前

    我们可以使用多态性(继承+虚拟函数)来在一个公共的基类型下概括不同的类型,然后引用不同的对象,就像它们是相同的类型一样。

    使用 dynamic_cast 似乎是完全相反的方法,因为本质上,我们在决定要采取什么行动之前,检查对象的特定类型。

    是否有任何已知的例子表明,常规多态性无法像使用 动态铸造 ?

    5 回复  |  直到 12 年前
        1
  •  6
  •   Christian Hackl    12 年前

    每当您发现自己想要在基类中使用“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())
        {
            // ...
        }
    }
    

    这看起来可能比 动态铸造 ,但仔细观察,你很快就会发现,除了“邪恶” 动态铸造 不再出现在代码中(前提是您没有使用未实现的古老编译器 动态铸造 ! :)). 更糟糕的是,“自行编写的动态转换”方法冗长、容易出错且重复,而 动态铸造 在类定义中没有任何额外的代码。

    因此,真正的问题应该是,是否存在这样的情况 基类知道具体派生类是有意义的 答案是:通常不会,但你肯定会遇到这样的情况。

    用非常抽象的术语思考一下软件的一个组件,它将对象从一个部分(a)传输到另一部分(B)。这些对象的类型 Class1 Class2 具有 类别2 是-a 类别1 .

    Class1
      ^
      |
      |
    Class2
    
    
    A - - - - - - - -> B
        (objects)
    

    B、 但是,只有 类别2 .B可以是系统的完全不同的部分,由不同的人编写,也可以是遗留代码。在这种情况下,您希望在不进行任何修改的情况下重用A-to-B通信,并且您也可能无法修改B。因此,明确询问您是否正在处理 类别1 类别2 对象。

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

    如果 类别2 不是叶类(即,如果可能存在从其进一步派生的类)。

    最终,这通常取决于您是从一开始就设计一个包含所有部分的整个系统,还是在后期修改或调整其中的部分。但如果你发现自己遇到了上述问题,你可能会感激 动态铸造 在正确的情况下,作为正确工作的正确工具。

        2
  •  1
  •   juanchopanza    12 年前

    它允许您执行只能对派生类型执行的操作。但这通常是一个提示,表明重新设计是正确的。

    struct Foo
    {
      virtual ~Foo() {}
    };
    
    struct Bar : Foo
    {
      void bar() const {}
    };
    
    int main()
    {
      Foo * f = new Bar();
      Bar* b = dynamic_cast<Bar*>(f);
      if (b) b->bar(); 
      delete f;
    }
    
        3
  •  1
  •   Mats Petersson    12 年前

    我想不出任何不可能使用虚拟功能的情况(除了诸如 boost:any 以及类似的“失去原始类型”作品)。

    然而,我发现自己在使用 dynamic_cast 在Pascal编译器中我用C++编写了几次。主要是因为这是一个“更好”的解决方案,而不是在基类中添加十几个虚拟函数,只有在你已经(应该)知道对象是什么类型的情况下,才在一两个地方使用 动态铸造 -其中一个可能是通过将类型实际存储为派生类型而不是基类型来“修复”的。

    在一些地方,我使用了 ArrayDecl* a = dynamic_cast<ArrayDecl*>(type); 以确定 type 实际上是一个数组声明,而不是在访问索引时使用非数组类型作为基的人(我也需要 a 以稍后访问阵列类型信息)。再次,将所有虚拟函数添加到基 TypeDecl 类将提供很多函数,这些函数通常不会返回任何有用的信息(例如。 NULL ),除非您已经知道该类是(或至少应该是)派生类型之一,否则不会调用。例如,了解数组的范围/大小对于不是数组的类型是无用的。

        4
  •  1
  •   Wojtek Surowka    12 年前

    真的没有优势。有时 dynamic_cast 对于快速破解是有用的,但通常最好正确设计类并使用多态性。有些情况下,由于某些原因,无法修改基类以添加必要的虚拟函数(例如,它来自我们不想修改的第三方),但仍然 动态铸造 用法应该是例外,而不是规则。 一个经常使用的论点是不方便将所有内容都添加到基类中,但这种论点实际上并不起作用,因为Visitor模式(参见例如。 http://sourcemaking.com/design_patterns/visitor/cpp/2 )纯粹通过多态性以一种更有组织的方式解决了这个问题——使用Visitor,您可以保持基类较小,并且仍然使用虚拟函数而无需强制转换。

        5
  •  0
  •   user2672165    12 年前

    dynamic_cast 当成员函数在基类中不可用,但仅在派生类中可用时,需要在基类指针上使用。使用它并没有什么好处。这是一种在基类中不重写虚拟函数时安全地向下转换的方法。检查返回值上的空指针。您是正确的,因为它在没有虚拟函数派生的地方使用。