Dynamic_Cast and Static_Cast in C++

Regular cast vs. static_cast vs. dynamic_cast

static_cast

static_cast is used for cases where you basically want to reverse an implicit conversion, with a few restrictions and additions. static_cast performs no runtime checks. This should be used if you know that you refer to an object of a specific type, and thus a check would be unnecessary. Example:

void func(void *data) {
// Conversion from MyClass* -> void* is implicit
MyClass *c = static_cast<MyClass*>(data);
...
}

int main() {
MyClass c;
start_thread(&func, &c) // func(&c) will be called
.join();
}

In this example, you know that you passed a MyClass object, and thus there isn't any need for a runtime check to ensure this.

dynamic_cast

dynamic_cast is useful when you don't know what the dynamic type of the object is. It returns a null pointer if the object referred to doesn't contain the type casted to as a base class (when you cast to a reference, a bad_cast exception is thrown in that case).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}

You can not use dynamic_cast for downcast (casting to a derived class) if the argument type is not polymorphic. For example, the following code is not valid, because Base doesn't contain any virtual function:

struct Base { };
struct Derived : Base { };
int main() {
Derived d; Base *b = &d;
dynamic_cast<Derived*>(b); // Invalid
}

An "up-cast" (cast to the base class) is always valid with both static_cast and dynamic_cast, and also without any cast, as an "up-cast" is an implicit conversion (assuming the base class is accessible, i.e. it's a public inheritance).

Regular Cast

These casts are also called C-style cast. A C-style cast is basically identical to trying out a range of sequences of C++ casts, and taking the first C++ cast that works, without ever considering dynamic_cast. Needless to say, this is much more powerful as it combines all of const_cast, static_cast and reinterpret_cast, but it's also unsafe, because it does not use dynamic_cast.

In addition, C-style casts not only allow you to do this, but they also allow you to safely cast to a private base-class, while the "equivalent" static_cast sequence would give you a compile-time error for that.

Some people prefer C-style casts because of their brevity. I use them for numeric casts only, and use the appropriate C++ casts when user defined types are involved, as they provide stricter checking.

dynamic_cast and static_cast in C++

Here's a rundown on static_cast<> and dynamic_cast<> specifically as they pertain to pointers. This is just a 101-level rundown, it does not cover all the intricacies.

static_cast< Type* >(ptr)

This takes the pointer in ptr and tries to safely cast it to a pointer of type Type*. This cast is done at compile time. It will only perform the cast if the types are related. If the types are not related, you will get a compiler error. For example:

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)

This again tries to take the pointer in ptr and safely cast it to a pointer of type Type*. But this cast is executed at runtime, not compile time. Because this is a run-time cast, it is useful especially when combined with polymorphic classes. In fact, in certian cases the classes must be polymorphic in order for the cast to be legal.

Casts can go in one of two directions: from base to derived (B2D) or from derived to base (D2B). It's simple enough to see how D2B casts would work at runtime. Either ptr was derived from Type or it wasn't. In the case of D2B dynamic_cast<>s, the rules are simple. You can try to cast anything to anything else, and if ptr was in fact derived from Type, you'll get a Type* pointer back from dynamic_cast. Otherwise, you'll get a NULL pointer.

But B2D casts are a little more complicated. Consider the following code:

#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() can't tell what kind of object CreateRandom() will return, so the C-style cast Bar* bar = (Bar*)base; is decidedly not type-safe. How could you fix this? One way would be to add a function like bool AreYouABar() const = 0; to the base class and return true from Bar and false from Foo. But there is another way: 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;

}

The casts execute at runtime, and work by querying the object (no need to worry about how for now), asking it if it the type we're looking for. If it is, dynamic_cast<Type*> returns a pointer; otherwise it returns NULL.

In order for this base-to-derived casting to work using dynamic_cast<>, Base, Foo and Bar must be what the Standard calls polymorphic types. In order to be a polymorphic type, your class must have at least one virtual function. If your classes are not polymorphic types, the base-to-derived use of dynamic_cast will not compile. Example:

class Base {};
class Der : public Base {};


int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile

return 0;
}

Adding a virtual function to base, such as a virtual dtor, will make both Base and Der polymorphic types:

class Base 
{
public:
virtual ~Base(){};
};
class Der : public Base {};


int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK

return 0;
}

When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?

static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones). In many cases, explicitly stating static_cast isn't necessary, but it's important to note that the T(something) syntax is equivalent to (T)something and should be avoided (more on that later). A T(something, something_else) is safe, however, and guaranteed to call the constructor.

static_cast can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast through virtual inheritance. It does not do checking, however, and it is undefined behavior to static_cast down a hierarchy to a type that isn't actually the type of the object.


const_cast can be used to remove or add const to a variable; no other C++ cast is capable of removing it (not even reinterpret_cast). It is important to note that modifying a formerly const value is only undefined if the original variable is const; if you use it to take the const off a reference to something that wasn't declared with const, it is safe. This can be useful when overloading member functions based on const, for instance. It can also be used to add const to an object, such as to call a member function overload.

const_cast also works similarly on volatile, though that's less common.


dynamic_cast is exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards – you can cast sideways or even up another chain. The dynamic_cast will seek out the desired object and return it if possible. If it can't, it will return nullptr in the case of a pointer, or throw std::bad_cast in the case of a reference.

dynamic_cast has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't using virtual inheritance. It also can only go through public inheritance - it will always fail to travel through protected or private inheritance. This is rarely an issue, however, as such forms of inheritance are rare.


reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another — such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions that reinterpret_cast cannot do, too. It's used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of a pointer to aligned data.


C-style cast and function-style cast are casts using (type)object or type(object), respectively, and are functionally equivalent. They are defined as the first of the following which succeeds:

  • const_cast
  • static_cast (though ignoring access restrictions)
  • static_cast (see above), then const_cast
  • reinterpret_cast
  • reinterpret_cast, then const_cast

It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a reinterpret_cast, and the latter should be preferred when explicit casting is needed, unless you are sure static_cast will succeed or reinterpret_cast will fail. Even then, consider the longer, more explicit option.

C-style casts also ignore access control when performing a static_cast, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.

static_cast vs dynamic_cast

Use static_cast. If you know that your Base* points to a Derived, then use static_cast. dynamic_cast is useful for when it might point to a derived.

dynamic_cast vs static_cast to void*

From 5.2.7 / 7:

If T is "pointer to cv void," then the result is a pointer to the most
derived object pointed to by v. Otherwise, a run-time check is applied
to see if the object pointed or referred to by v can be converted to
the type pointed or referred to by T.

So using dynamic_cast<void*>(o) you get a pointer to the first byte of the most "derived" object (if o is polymorphic).

The code the compiler generates for dynamic_cast<void *>(...) is something like:

static_cast<void*>(dynamic_cast<most_derived_type *>(...))

This property is often used for serialization.

static_cast and RTTI vs dynamic_cast

It is faster to test the type and then do the static_cast, but the operations are not equivalent as that will only allow downcast to the most derived type (any intermediate level will not be matched with the typeid). I would use dynamic_cast as it is more robust (will not break if someone extends your type and passes a pointer, for example).

If performance of dynamic_cast is an issue in your application, you should reconsider the design. While typeid + static_cast is faster than dynamic_cast, not having to switch on the runtime type of the object is faster than any of them.

static_cast/dynamic_cast: Does it change the object?

The casts don't change the object at all. They only give you a different pointer to a related class type in the inheritance hierarchy:

Derived x;

Base * p = &x;
AnotherClass * q = dynamic_cast<AnotherClass*>(p);

// q may or may not be NULL

For example, the above dynamic cast succeeds if we have a hierarchy AnotherClass : Base and Derived : AnotherClass (and Base is polymorphic).

A static_cast can usually be used when you already know that you have a more derived dynamic type, but happen to have only a pointer or reference to a base.

(A static cast can never be used to cast from a virtual base, in which case you always need dynamic_cast.)

static cast versus dynamic cast

Use dynamic_cast when casting from a base class type to a derived class type. It checks that the object being cast is actually of the derived class type and returns a null pointer if the object is not of the desired type (unless you're casting to a reference type -- then it throws a bad_cast exception).

Use static_cast if this extra check is not necessary. As Arkaitz said, since dynamic_cast performs the extra check, it requires RTTI information and thus has a greater runtime overhead, whereas static_cast is performed at compile-time.



Related Topics



Leave a reply



Submit