dynamic_cast如何工作?

Bob*_*ohn 13 c++ dynamic-cast dynamic

如果您有以下内容:

class Animal{};

class Bird : public Animal{};

class Dog : public Animal{};

class Penguin : public Bird{};

class Poodle : public Dog{};
Run Code Online (Sandbox Code Playgroud)

dynamic_cast只检查一个类是否是另一个类的派生类,或者一个类是否是另一个类的基类?所以,如果我有:

Bird* bird;
Animal* animal;

bird = dynamic_cast<Animal*>(bird);
animal = dynamic_cast<Bird*>(animal);
Run Code Online (Sandbox Code Playgroud)

bird现在指向一个Animal类,以便我可以使用bird->some_function();它将调用该函数Animal?而animal现在指向一Bird类,所以我可以做animal->some_function();,它会调用some_function();Bird

我一直试图弄清楚dynamic_cast我在网上找到的作品和资源是如何最有帮助的.如果有人能够提供其它功能的其他见解dynamic_cast以及某些有用的实例,我将非常感激.

Kir*_*lev 15

关于动态强制转换最重要的是它应该应用于a polymorphic type.没有那个动态演员就像静态演员一样.

什么是多态类型?具有至少一个虚拟方法或虚拟析构函数或虚拟基类的任何类都是多态的.只有那些类型virtual method table的数据布局中有(VMT).没有虚拟内容的类没有VMT.该标准没有说明应该如何实现多态和虚方法,但据我所知,所有编译器都是这样做的.

在您的示例中,类不是多态的.在我看来,如果动态强制转换应用于非多态类型,编译器会发出错误会更好.然而,他们不这样做.这增加了混乱.

所有类的VMT指针都不同.这意味着在运行时查看:

Animal* animal;
Run Code Online (Sandbox Code Playgroud)

可以知道对象的真实类是什么.它是一个Bird或一个Dog或其他东西.根据VMT的值知道实际类型,生成的代码可以在需要时进行调整.

这是一个例子:

class Animal   { virtual ~Animal();   int m1; };
class Creature { virtual ~Creature(); int m2; };

class Bird : public Animal, Creature { };

Bird   *bird = new Bird();
Creature *creature = dynamic_cast<Creature*>(bird);
Run Code Online (Sandbox Code Playgroud)

请注意,creature不是第一个基类.这意味着指针将移动到指向对象的右侧部分.不过,以下内容仍然有效:

Animal *animal = dynamic_cast<Animal*>(creature);   // Case2.
Run Code Online (Sandbox Code Playgroud)

因为生物的VMT当它是其他类的一部分时,它对于独立使用时对象的VMT是不一样的:

Creature *creature1 = new Creature();
Run Code Online (Sandbox Code Playgroud)

这种区别允许正确实现动态转换.在示例中Case2,指针将被移回.我测试了这个.这有效.


Gre*_*ill 8

dynamic_cast操作员检查的类型实际对象由指针指向.这使它与编译时不同static_cast; 结果dynamic_cast取决于运行时数据.

dynamic_cast<Animal*>(bird)
Run Code Online (Sandbox Code Playgroud)

在上述情况下,Animal是一个超Bird所以dynamic_cast在此没有必要(和编译器会把它同样作为static_cast或不投的话).

dynamic_cast<Bird*>(animal)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当实际执行此语句时,运行时系统将检查实际animal指向的任何类型对象的实际类型.它可能是一个Bird或子类Bird,在这种情况下结果将是有效的Bird*.如果对象不是a Bird,那么结果将是NULL.

您将这些dynamic_cast调用的结果分配回原始指针的事实使您的问题变得更加复杂.这可能是混淆的一部分来自,我从上面的讨论中省略了这个方面.


lef*_*out 5

正如你所说,这没有多大意义。

重点dynamic_cast是在运行时解决多态性。所以实际有趣的场景会是这样的

void animalhandler(Animal& animal);
Run Code Online (Sandbox Code Playgroud)

然而,它不是(至少不仅是)用 的实例调用Animal,而是用任何子类调用。您通常甚至不需要知道:您可以调用 的任何虚拟成员,animal并确保 C++ 调用正确的重载,无论派生类*animal实际属于什么。

但有时您希望做一些只能用一个特定的派生实例才能实现的事情。在这种情况下,您可以使用dynamic_cast,例如

void animalhandler(Animal& animal) {
  if(auto as_bird = dynamic_cast<Bird*>(&animal)) {
    // bird-specific code
  }
}
Run Code Online (Sandbox Code Playgroud)

其中if只有如果火灾animal实际上是一个Bird(或衍生的Bird),否则dynamic_cast只返回nullptrif解释为false

现在,你想出了相反的想法。让我们看看这会是什么样子:

  if(auto as_bird = dynamic_cast<Bird*>(&animal)) {
    if(auto as_animal = dynamic_cast<Animal*>(as_bird)) {
      // animal-specific code
    }
  }
Run Code Online (Sandbox Code Playgroud)

……等等,动物特异性有什么意义吗?不,因为所有 Birds 都是Animals,我们知道在编译时所以没有必要动态检查它。您仍然可以编写它,但您最好将其省略并as_bird直接使用,因为它可以访问所有会这样做的成员as_animal