user626201
user626201

Reputation: 1682

Casting pointer-to base class into a pointer-to derived class

I have read a bit about casting in C++. Coming from a C background, using normal (type) casting is common for things like void * but for C++, there are dynamic_cast, reinterpret_cast, static_cast, etc.

The problem/issue/question is about which of the above casts should be used when a conversion between a base pointer and a derived pointer.

Our data storage stores a pointer to a base class (B). The functions allocate the derived pointers (D).

The code example is as follows:

class B
{ int _some_data; }
class D : public B
{ int _some_more_data; }

The code then looks something like:

D *obj = new D;
obj->_some_data = 1;
obj->_some_more_data = 2;
<store obj>

Then later when we access the data:

B *objB = <get out data>
if (objB->_some_data == 1)
{ D *objD = (D *) objB; <do some processing> }

Now the cast I am concerned about is D *objD = (D *) objB.

Which cast should we be using?

Thanks.

Upvotes: 5

Views: 18521

Answers (3)

Sergey
Sergey

Reputation: 79

In this case you should use dynamic_cast.

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145204

For related types that you know about but the compiler don't, use a static_cast.

But in your case you should not cast at all.

You write that

Our data storage stores a pointer to a base class (B). The functions allocate the derived pointers (D).

That is to throw away information, which is not a good idea. Someone realized that it's not a good idea, that in fact it could not work, and therefore tried to keep that type information dynamically in the value of B::_some_data. The total effect: to throw away the C++ support for handling that type information, and substituting a very fragile and dirty homegrown solution.

In order to leverage the C++ support, make B a polymorphic class, i.e. with at least one virtual member:

struct B { virtual ~B() {} };

I removed the data member _some_data since apparently its only purpose was to keep track of the dynamic type, and now the C++ support does that, in practice via a so called "vtable pointer" in the object. The total object size is probably the same. The bug attraction and sheer ugliness is, however, reduced by some orders of magnitude. ;-)

Then,

struct D
    : B
{
    int some_more_data_;
    D( int v ): some_more_data_( v ) {}
};

And then, with polymorphic classes, your processing code can use a safe dynamic_cast, as follows:

B* const objB = getOutData();
if( D* const objD = dynamic_cast<D*>( objB ) )
{
    // do some processing using objD
}

Now this manual dynamic type checking is still very dirty and reflects a non-OO system architecture. With object oriententation, instead of operations checking what kind of data they have been given, the data effectively contains pointers to appropriate specialized operations. But I think it might be best to take one step at a time, and as a first step the above: getting rid of the fragile bug-attracting homegrown dynamic type checking scheme, and using relatively clean super-duper fresh nice-smelling good looking etc. C++ support for that. :-)

Upvotes: 3

Luchian Grigore
Luchian Grigore

Reputation: 258548

In this case, no cast is truly safe.

The safest would be dynamic_cast, but your objects aren't polymorphic so it doesn't apply here. But you should consider at least having a virtual destructor, as I can see you plan on extending the classes.

static_cast is not safe, as pointed out by this msdn page:

class B {};

class D : public B {};

void f(B* pb, D* pd) {
   D* pd2 = static_cast<D*>(pb);   // not safe, pb may
                                   // point to just B

   B* pb2 = static_cast<B*>(pd);   // safe conversion
}

reinterpret_cast also has no checks, so don't rely on it.

Also, casting to a pointer of a derived class is at least a code smell, if you need to do this it's 99% sure you have a flawed design.

Upvotes: 7

Related Questions