Если у вас было следующее:
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
и в некоторых случаях, когда это было бы полезно, я был бы очень признателен.
Самое важное в динамическом приведении является то, что он должен быть применен к 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
указатель будет смещен назад. Я проверял это. Это работает.
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
перезванивает на оригинальный указатель. Это, возможно, отсюда и часть путаницы, и я пропустил этот аспект в обсуждении выше.
Это не имеет особого смысла, как вы выразились.
Точка 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
с Animal
s, мы знаем это во время компиляции, поэтому нет смысла проверять это динамически. Вы все еще можете написать это, но вы могли бы также оставить это и использовать as_bird
напрямую, так как он дает доступ всем членам, которые as_animal
было бы.
Из рабочего проекта 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
работает с полиморфными классамиНадеюсь, это поможет:
#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;
}