Вот краткое изложение static_cast<>
и, в dynamic_cast<>
частности, как они относятся к указателям. Это всего лишь 101-кратное изложение, оно не охватывает все тонкости.
static_cast <Type *> (ptr)
Это берет указатель ptr
и пытается безопасно привести его к указателю типа Type*
. Это приведение сделано во время компиляции. Он будет выполнять приведение только в том случае, если типы типов связаны между собой. Если типы не связаны, вы получите ошибку компилятора. Например:
class B {};
class D : public B {};
class X {};
int main()
{
D* d = new D;
B* b = static_cast<B*>(d); // this works
X* x = static_cast<X*>(d); // ERROR - Won't compile
return 0;
}
dynamic_cast <Type *> (ptr)
Это снова пытается принять указатель ptr
и безопасно привести его к указателю типа Type*
. Но это приведение выполняется во время выполнения, а не во время компиляции. Поскольку это приведение во время выполнения, оно полезно, особенно в сочетании с полиморфными классами. Фактически, в некоторых случаях классы должны быть полиморфными, чтобы состав был законным.
Приведения могут идти в одном из двух направлений: от основания к производному (B2D) или от производного к базе (D2B). Достаточно просто увидеть, как будет выполняться приведение D2B во время выполнения. Либо ptr
было получено из, Type
либо нет. В случае D2B dynamic_cast <> правила просты. Вы можете попытаться привести что-либо к чему-либо еще, и, если оно ptr
действительно было получено из Type
, вы получите Type*
указатель обратно dynamic_cast
. В противном случае вы получите нулевой указатель.
Но броски B2D немного сложнее. Рассмотрим следующий код:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0; // pure virtual
virtual ~Base() {};
};
class Foo : public Base
{
public:
virtual void DoIt() { cout << "Foo"; };
void FooIt() { cout << "Fooing It..."; }
};
class Bar : public Base
{
public :
virtual void DoIt() { cout << "Bar"; }
void BarIt() { cout << "baring It..."; }
};
Base* CreateRandom()
{
if( (rand()%2) == 0 )
return new Foo;
else
return new Bar;
}
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = (Bar*)base;
bar->BarIt();
}
return 0;
}
main()
Я не могу сказать, какой тип объекта CreateRandom()
вернется, поэтому приведение в стиле C Bar* bar = (Bar*)base;
определенно небезопасно. Как ты мог это исправить? Одним из способов было бы добавить функцию типа bool AreYouABar() const = 0;
в базовый класс и возвращаться true
из Bar
и false
из Foo
. Но есть и другой способ: use dynamic_cast<>
:
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = dynamic_cast<Bar*>(base);
Foo* foo = dynamic_cast<Foo*>(base);
if( bar )
bar->BarIt();
if( foo )
foo->FooIt();
}
return 0;
}
Приведения выполняются во время выполнения и работают путем запроса объекта (пока не нужно беспокоиться о том, как это сделать), спрашивая, соответствует ли он типу, который мы ищем. Если это так, dynamic_cast<Type*>
возвращает указатель; в противном случае возвращается NULL.
Для того, чтобы это приведение от основания к производному работало с использованием dynamic_cast<>
, Base, Foo и Bar должны быть тем, что Стандарт называет полиморфными типами . Чтобы быть полиморфным типом, ваш класс должен иметь хотя бы одну virtual
функцию. Если ваши классы не являются полиморфными типами, базовое-производное использование dynamic_cast
не будет компилироваться. Пример:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
Добавление виртуальной функции к базе, такой как виртуальный dtor, приведет к полиморфным типам Base и Der:
class Base
{
public:
virtual ~Base(){};
};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK
return 0;
}
dynamic_cast<>
работает негласно (или о том, как работает C ++), хорошей книгой (которую также довольно легко прочитать для чего-то такого технического) является книга Липпмана «Внутри объектной модели C ++». Также книги Страуструпа «Дизайн и эволюция C ++» и «Язык программирования C ++» являются хорошими источниками, но книга Липпмана посвящена тому, как C ++ работает «за кулисами».