Вот краткое изложение по static_cast<>
и dynamic_cast<>
конкретно, так как они относятся к указателям. Это всего лишь 101-кратное изложение, оно не охватывает все тонкости.
static_cast <Тип *> (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 <Тип *> (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
. Но есть и другой способ: используйте 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;
}