C++ dynamic_cast是如何工作的?

C++ dynamic_cast是如何工作的?,c++,dynamic,dynamic-cast,C++,Dynamic,Dynamic Cast,如果您有以下情况: class Animal{}; class Bird : public Animal{}; class Dog : public Animal{}; class Penguin : public Bird{}; class Poodle : public Dog{}; dynamic\u cast只是检查一个类是另一个的派生类,还是一个类是另一个的基类?如果我有: Bird* bird; Animal* animal; bird = dynamic_cast<

如果您有以下情况:

class Animal{};

class Bird : public Animal{};

class Dog : public Animal{};

class Penguin : public Bird{};

class Poodle : public Dog{};
dynamic\u cast
只是检查一个类是另一个的派生类,还是一个类是另一个的基类?如果我有:

Bird* bird;
Animal* animal;

bird = dynamic_cast<Animal*>(bird);
animal = dynamic_cast<Bird*>(animal);
Bird*Bird;
动物*动物;
bird=动态施法(bird);
动物=动态铸型(动物);
bird
现在将指向一个
Animal
类,这样我就可以使用
bird->some_函数()
并且它将调用
Animal
中的函数?现在,
animal
指向一个
Bird
类,所以我可以做
animal->some_function()some_函数()
Bird


我一直在试图弄清楚
dynamic\u cast
是如何工作的,而我在网上找到的资源并不是最有用的。如果有人能对
dynamic\u cast
的功能和一些有用的实例提供其他见解,我将不胜感激。

关于dynamic cast,最重要的是它应该应用于
多态类型
。如果没有这一点,动态强制转换就像静态强制转换一样工作

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

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

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

Animal* animal;
可以知道对象的真实类是什么。它是鸟还是狗还是别的什么东西。根据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);

这种区别允许正确实现动态强制转换。在示例
Case2
中,指针将向后移动。我测试了这个。这是可行的。

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

dynamic\u cast
的要点是在运行时解析多态性。因此,真正有趣的场景是

void animalhandler(Animal& animal);
但是,它不是(至少不仅)用
Animal
的实例调用的,而是用任何子类调用的。您通常不需要知道:您可以调用任何代码成员<代码>动物< /代码>,并确保C++调用正确的重载,因为派生类<代码> *动物< /代码>实际上属于。 但有时您希望只对一个特定的派生实例执行某些操作。在这种情况下,可以使用
dynamic\u cast
,如

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

…等等,这意味着什么是动物特有的吗?不,因为所有的
Bird
s都是
Animal
s,所以我们知道在编译时,动态检查它是没有意义的。您仍然可以编写它,但您也可以将其省略,直接使用
作为\u bird
,因为它可以像\u animal
那样访问所有成员。

动态\u cast
操作符检查指针指向的实际对象的类型。这就是它不同于编译时的原因;
dynamic_cast
的结果取决于运行时数据

dynamic_cast<Animal*>(bird)
在这种情况下,当实际执行此语句时,运行时系统将检查实际指向的任何类型的对象
animal
。它可能是
Bird
Bird
的子类,在这种情况下,结果将是有效的
Bird*
。如果对象不是
,则结果将为
NULL


由于您将这些
dynamic\u cast
调用的结果分配回原始指针,您的问题变得更加复杂。这也许是混淆的一部分,我从上面的讨论中省略了这方面。

< >从C++工作草案

Dynamic cast[expr.Dynamic.cast]

1表达式dynamic_cast(v)的结果是将表达式v转换为类型T的结果。T应是指向完整类类型的指针或引用,或“指向cv void的指针”。dynamic_cast运算符不得丢弃常量(5.2.11)

6否则,v应为多态类型(10.3)的指针或左值

8如果C是T指向或引用的类类型,则运行时检查按如下逻辑执行:
-如果在v指向(引用)的最派生对象中,v指向(引用)C对象的公共基类子对象,并且如果只有一个C类型的对象是从v指向(引用)的子对象派生的,则结果指向(引用)该C对象。
-否则,如果v指向(引用)最派生对象的公共基类子对象,并且最派生对象的类型具有类型为C的基类(明确且公开),则结果指向(引用)最派生对象的C子对象。
-否则,运行时检查将失败

从这些条款中你能得出什么结论

  • dynamic\u cast
    适用于多态类
  • 它在运行时查看指向(或引用)的对象
  • 它根据指向的对象的公共基类决定强制转换是否成功

您投错了。
bird
animal
已经是指针,但您正在记录它们的地址。@Olaf。感谢您的编辑。也许您应该删除您的注释。它确实会发出错误
C2683:
void animalhandler(Animal& animal) {
  if(auto as_bird = dynamic_cast<Bird*>(&animal)) {
    // bird-specific code
  }
}
  if(auto as_bird = dynamic_cast<Bird*>(&animal)) {
    if(auto as_animal = dynamic_cast<Animal*>(as_bird)) {
      // animal-specific code
    }
  }
dynamic_cast<Animal*>(bird)
dynamic_cast<Bird*>(animal)