dynamic_cast如何工作?

时间:2012-12-09 00:08:48

标签: c++ dynamic 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功能的其他见解以及某些有用的实例,我会非常感激。

5 个答案:

答案 0 :(得分:12)

关于动态广播的最重要的事情是它应该应用于polymorphic type。没有那个动态演员就像静态演员一样。

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

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

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

Animal* animal;

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

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

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

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

Creature *creature1 = new Creature();

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

答案 1 :(得分:7)

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

dynamic_cast<Animal*>(bird)

在上面的例子中,AnimalBird的超类,因此这里不需要dynamic_cast(并且编译器将其视为与static_cast相同或不总而言之。)

dynamic_cast<Bird*>(animal)

在这种情况下,当实际执行此语句时,运行时系统将检查实际指向的任何类型的对象animal的实际类型。它可能是Bird的{​​{1}}或子类,在这种情况下,结果将是有效的Bird。如果对象不是Bird*,则结果将为Bird

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

答案 2 :(得分:2)

当你说出来时,这没有多大意义。

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只返回{{1} } nullptr解释为if

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

false

......等等,这是否意味着什么是动物特有的?不,因为所有 if(auto as_bird = dynamic_cast<Bird*>(&animal)) { if(auto as_animal = dynamic_cast<Animal*>(as_bird)) { // animal-specific code } } 都是Bird,我们知道在编译时所以没有必要动态地检查它。您仍然可以编写它,但您也可以将其删除并直接使用Animal,因为它可以访问as_bird所有成员。

答案 3 :(得分:0)

来自C ++工作草案

  

动态广告[expr.dynamic.cast]

     

1表达式dynamic_cast&lt; T&gt;(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适用于多态类
  • 它在指向(或引用)
  • 的对象上查看 runtime
  • 它根据指向的对象的 public 基类决定,无论是强制转换成功还是失败

答案 4 :(得分:-1)

我希望这会有所帮助:

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