18

如果您有以下情况:

class Animal{};

class Bird : public Animal{};

class Dog : public Animal{};

class Penguin : public Bird{};

class Poodle : public Dog{};

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

Bird* bird;
Animal* animal;

bird = dynamic_cast<Animal*>(bird);
animal = dynamic_cast<Bird*>(animal);

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

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

4

4 回答 4

18

动态转换最重要的一点是它应该应用于polymorphic type. 没有它,动态演员表就像静态演员表一样工作。

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

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

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

Animal* animal;

可以知道对象的真正类别是什么。是aBird还是aDog还是别的什么。从 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);

请注意,生物不是第一个基类。这意味着指针将移动到指向对象的右侧部分。尽管如此,以下内容仍然有效:

Animal *animal = dynamic_cast<Animal*>(creature);   // Case2.

因为当 Creature 是其他类的一部分时,它的 VMT 与单独使用时对象的 VMT 不同:

Creature *creature1 = new Creature();

这种区别允许正确实现动态转换。在示例Case2中,指针将向后移动。我对此进行了测试。这行得通。

于 2012-12-09T00:26:33.787 回答
10

dynamic_cast运算符检查指针所指向的实际对象的类型。这就是它与 compile-time 不同的原因static_cast;结果dynamic_cast取决于运行时数据。

dynamic_cast<Animal*>(bird)

在上述情况下,这里不需要soAnimal的超类(编译器会将其视为相同或根本不进行强制转换)。Birddynamic_caststatic_cast

dynamic_cast<Bird*>(animal)

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

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

于 2012-12-09T00:28:06.500 回答
5

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

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

void animalhandler(Animal& animal);

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

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

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

其中ifonly 触发 ifanimal实际上是 a Bird(或派生自Bird),否则仅dynamic_cast返回解释为.nullptriffalse

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

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

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

于 2012-12-09T00:27:25.573 回答
0

来自 C++ 工作草案

动态转换 [expr.dynamic.cast]

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

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

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

您可以从这些条款中得出什么结论

  • dynamic_cast适用于多态类
  • 它在运行时查看指向(或引用)的对象
  • 它根据所指向对象的公共基类决定强制转换是成功还是失败
于 2012-12-09T00:40:15.663 回答