Как работает 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 и в некоторых случаях, когда это было бы полезно, я был бы очень признателен.

11

Решение

Самое важное в динамическом приведении является то, что он должен быть применен к 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);

Обратите внимание, что существо не первый базовый класс. Это означает, что указатель будет смещен, чтобы указывать на правую часть объекта. Тем не менее, следующие будут работать:

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

потому что VMT Существа, когда он является частью другого класса, не будет таким же, как VMT объекта, когда он используется отдельно:

Creature *creature1 = new Creature();

Это различие позволяет правильно реализовать динамическое приведение. В примере Case2 указатель будет смещен назад. Я проверял это. Это работает.

11

Другие решения

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 перезванивает на оригинальный указатель. Это, возможно, отсюда и часть путаницы, и я пропустил этот аспект в обсуждении выше.

6

Это не имеет особого смысла, как вы выразились.

Точка 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

Из рабочего проекта C ++

Динамическое приведение [expr.dynamic.cast]

1 Результат выражения dynamic_cast<T>(v) является результатом преобразования выражения v в тип T. T должен быть указателем или ссылкой на полный тип класса или «указателем на cv void». Оператор dynamic_cast не должен отбрасывать константу (5.2.11).

6 В противном случае v должен быть указателем или значением l полиморфного типа (10.3).

8 Если C является типом класса, на который указывает или ссылается T, проверка во время выполнения логически выполняется следующим образом:
— Если в самом производном объекте, на который указывает (ссылается) v, v указывает (ссылается) на подобъект общедоступного базового класса объекта C, и если только один объект типа C получен из подобъекта, на который указывает (ссылается) v результат указывает (ссылается) на этот объект C.
— Иначе, если v указывает (ссылается) на подобъект общедоступного базового класса самого производного объекта, а тип самого производного объекта имеет однозначный и общедоступный базовый класс типа C, результат указывает (ссылается) на подобъект C самого производного объекта.
— В противном случае проверка во время выполнения завершится неудачно.

Что вы можете сделать из этих пунктов

  • dynamic_cast работает с полиморфными классами
  • это смотрит на время выполнения на объекте, указанном (или упомянутом)
  • он решает на основе общественности базовые классы объекта, на который указывает, успешно ли приведен акт
0

Надеюсь, это поможет:

#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 iamsystem("pause");
return 0;
}
-1
По вопросам рекламы [email protected]