繁体   English   中英

使用dynamic_cast而不是传统的多态性有什么好处?

[英]What is the advantage of using dynamic_cast instead of conventional polymorphism?

我们可以使用多态(继承+虚函数)来泛化公共基类型下的不同类型,然后引用不同的对象,就好像它们属于同一类型一样。

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

有没有任何已知的例子可以像使用dynamic_cast一样容易地用传统的多态实现?

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

这可能看起来比dynamic_cast更清晰,但仔细观察后,你很快就会意识到除了你的代码中不再出现“邪恶”的dynamic_cast之外你没有获得任何东西(前提是你没有使用古代编译器没有实现dynamic_cast !:))。 更糟糕的是 - “自编动态强制转换”方法冗长,容易出错且重复,而dynamic_cast可以正常工作,在类定义中没有任何额外的代码。

所以真正的问题应该是,是否存在基类知道具体派生类的情况 答案是:通常它没有,但你无疑会遇到这种情况。

用非常抽象的术语来思考一下软件的一个组件,它将对象从一个部分(A)传输到另一个部分(B)。 这些对象的类型为Class1Class2Class2 Class1

Class1
  ^
  |
  |
Class2


A - - - - - - - -> B
    (objects)

但是,B只对Class2有一些特殊处理。 B可以是系统的完全不同的部分,由不同的人或遗留代码编写。 在这种情况下,您希望重复使用A-to-B通信而不进行任何修改,您也可能无法修改B. 因此,明确询问您是否在该行的另一端处理Class1Class2对象可能是有意义的。

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

如果Class2不是叶子类(即,如果可能存在进一步派生的类),这可能是更可取的。

最后,它通常归结为您是从一开始就设计一个包含所有部件的整个系统,还是必须在稍后阶段修改或调整其中的一部分。 但是如果你发现自己遇到了类似上面的问题,你可能会认为dynamic_cast是在正确的情况下正确工作的正确工具。

它允许您执行只能对派生类型执行的操作。 但这通常暗示重新设计是有序的。

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

我无法想到任何不可能使用虚函数的情况(除了诸如boost:any类的东西boost:any和类似的“丢失原始类型”工作)。

但是,我发现自己在Pascal编译器中使用了dynamic_cast几次,我目前用C ++编写。 主要是因为它比将十几个虚函数添加到基类是一个“更好”的解决方案,当你已经(应该)知道对象是什么类型时,它只在一两个地方使用。 目前,在大约4300行代码中,有6个dynamic_cast实例 - 其中一个实例可能通过实际存储类型作为派生类型而不是基类型来“修复”。

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

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

当成员函数在基类中不可用时,需要在基类指针上使用dynamic_cast进行向下转换,但仅在派生类中可用。 使用它没有任何好处。 当从基类中重写虚函数时,这是一种安全向下转换的方法。 检查返回值上的空指针。 你是正确的,它用于没有虚函数推导的地方。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM