How does dynamic_cast work?

孤人 提交于 2021-02-05 20:25:38

问题


If you had the following:

class Animal{};

class Bird : public Animal{};

class Dog : public Animal{};

class Penguin : public Bird{};

class Poodle : public Dog{};

Does dynamic_cast just check if one class is a derived class of another, or if one class is a base class of another? So if I had:

Bird* bird;
Animal* animal;

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

bird would now point to an Animal class, so that I can use bird->some_function(); and it will call the function in Animal? And animal now points to a Bird class, so I can do animal->some_function(); and it will call some_function(); in Bird?

I've been trying to figure out how the dynamic_cast works, and the resources I've found online haven't been the most helpful. If someone can offer other insight into the functionality of dynamic_cast and some instances in which it would be useful, I'd highly appreciate it.


回答1:


The most important thing about the dynamic cast is that it should be applied to a polymorphic type. Without that, dynamic cast works like a static cast.

What is a polymorphic type? Any class that has at least one virtual method or virtual destructor or virtual base class is polymorphic. Only those types have a virtual method table (VMT) in their data layout. Classes that do not have anything virtual do not have VMT's. The standard does not say how polymorphism and virtual methods should be implemented, yet all compilers, as far as I know, do this.

In your examples classes are not polymorphic. In my opinion, it would be better if compilers would issue an error when the dynamic cast is applied to a non-polymorphic type. Nevertheless, they do not do this. This adds to the confusion.

VMT pointers for all classes are different. This means that on the runtime looking at:

Animal* animal;

it is possible to know what the real class of the object is. Is it a Bird or a Dog or something else. Knowing the real type from the value of VMT, generated code can make an adjustment if this is needed.

Here is an example:

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);

Note that creature is not the first base class. This means that the pointer will be shifted to point to the right part of the object. Nevertheless, the following will still be working:

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

because VMT of Creature when it is part of other class will not be the same to VMT of the object when it is used stand-alone:

Creature *creature1 = new Creature();

This distinction allows a proper implementation of a dynamic cast. In the example Case2 the pointer will be shifted back. I tested this. This works.




回答2:


The dynamic_cast operator checks the type of the actual object pointed to by the pointer. This is what makes it different from compile-time static_cast; the result of dynamic_cast depends on runtime data.

dynamic_cast<Animal*>(bird)

In the above case, Animal is a superclass of Bird so dynamic_cast is not necessary here (and the compiler will treat it the same as a static_cast or no cast at all).

dynamic_cast<Bird*>(animal)

In this case, when this statement is actually executed, the runtime system will inspect the actual type of whatever kind of object animal actually points to. It might be a Bird or subclass of Bird, in which case the result will be a valid Bird*. If the object is not a Bird, then the result will be NULL.

Your question is further complicated by the fact that you're assigning the result of these dynamic_cast calls back to the original pointer. This is perhaps where part of the confusion comes from, and I have omitted this aspect from the above discussion.




回答3:


This doesn't make much sense as you put it.

The point of dynamic_cast is to resolve polymorphism at runtime. So the actual interesting scenario would be something like

void animalhandler(Animal& animal);

which is however not (at least not only) called with instances of Animal, but with any of the subclasses. You often don't even need to know: you can call any virtual members of animal and be sure that C++ calls the correct overload, for whatever derived class *animal actually belongs to.

But sometimes you wish to do something that's only possible with one particular derived instance. In that case, you use dynamic_cast, like

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

where the if only fires if animal is in fact a Bird (or derived of Bird), otherwise the dynamic_cast just returns nullptr which the if interprets as false.

Now, you come up with the idea to do the opposite. Let's see what this would look like:

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

...wait, does it mean anything to be animal-specific? Nope, because all Birds are Animals, we know that at compile-time so there's no point checking it dynamically. You can still write it, but you might as well leave it out and use as_bird directly, since it gives access to all members that as_animal would.




回答4:


From the C++ Working Draft

Dynamic cast [expr.dynamic.cast]

1 The result of the expression dynamic_cast<T>(v) is the result of converting the expression v to type T. T shall be a pointer or reference to a complete class type, or "pointer to cv void.” The dynamic_cast operator shall not cast away constness (5.2.11).

6 Otherwise, v shall be a pointer to or an lvalue of a polymorphic type (10.3).

8 If C is the class type to which T points or refers, the run-time check logically executes as follows:
- If, in the most derived object pointed (referred) to by v, v points (refers) to a public base class subobject of a C object, and if only one object of type C is derived from the subobject pointed (referred) to by v the result points (refers) to that C object.
- Otherwise, if v points (refers) to a public base class subobject of the most derived object, and the type of the most derived object has a base class, of type C, that is unambiguous and public, the result points (refers) to the C subobject of the most derived object.
- Otherwise, the run-time check fails.

What you can conclude from these clauses

  • dynamic_cast works with polymorphic classes
  • it looks at runtime at the object pointed (or referred) to
  • it decides based on public base classes of the object pointed to, whether the cast succeeds or fails


来源:https://stackoverflow.com/questions/13783312/how-does-dynamic-cast-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!