问题 dynamic_cast如何工作?


如果您有以下内容:

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(); 它将调用该函数 Animal?和 animal 现在指向一个 Bird 上课,所以我能做到 animal->some_function(); 它会打电话 some_function(); 在 Bird

我一直想弄清楚怎么样 dynamic_cast 工作,我在网上找到的资源并不是最有帮助的。如果有人可以提供其他功能的洞察力 dynamic_cast 以及一些有用的例子,我非常感激。


5974
2017-12-09 00:08


起源

你的演员是错的。 bird 和 animal 已经是指针,但你正在拿他们的地址。 - Nikos C.
@Olaf。谢谢你的编辑。也许你应该删除你的评论。 - Kirill Kobelev


答案:


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

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

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

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

Animal* animal;

可以知道对象的真实类是什么。是一个 Bird 或者a 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);

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

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

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

Creature *creature1 = new Creature();

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


9
2017-12-09 00:26





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

dynamic_cast<Animal*>(bird)

在上述情况下, Animal 是一个超类 Bird 所以 dynamic_cast 这里没有必要(编译器会把它当作一个对待它 static_cast 或根本没有演员。

dynamic_cast<Bird*>(animal)

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

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


4
2017-12-09 00:28





你提出这个没有多大意义。

重点 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
  }
}

在哪里 if 只有 animal 实际上是一个 Bird (或衍生的 Bird),否则 dynamic_cast 回来 nullptr 哪个 if 解释为 false

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

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

......等等,它是否意味着任何动物特定的东西?不,因为 所有  Bird是的 Animals,我们知道在编译时,所以没有必要动态地检查它。你仍然可以写它,但你也可以把它留下来使用 as_bird 直接,因为它可以访问所有成员 as_animal 将。


2
2017-12-09 00:27





来自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 适用于多态类
  • 看着 运行 在指向(或引用)的对象处
  • 它决定基于 上市 指向的对象的基类,无论是强制转换成功还是失败

0
2017-12-09 00:40





我希望这有帮助:

#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>

using namespace std;

class A{
public:
    A(){}
    virtual void write() const = 0;
};

class B : public A{
public:
    B(){}
    void write() const { cout << "I'm B" << endl; }
    void iam(){ cout << "Yes, I am" << endl; }
};

int main(){
    B b;

    A* a;
    a = &b;

    b.write();
    b.iam();

    a->write();
    //a->iam(); A don't have a method iam


    system("pause");
    return 0;
}

-1
2017-12-09 00:34



它怎么会有帮助?似乎没有努力回答这个问题,你刚刚发布了一些不相关的代码。为什么你甚至包括矢量,算法或实用程序? - jirigracik