Sign in to follow this  

dynamic_cast and static_cast

This topic is 4866 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Alright, so I learned a new trick... Using dynamic_cast to access child-class methods from a parent class and using static_cast to access parent-class methods from a child class.
class CParent
{
public:
    virtual void Init()
    {
        printf("CParent::Init()\n");
    }
};

class CChild : public CParent
{
public:
    virtual void Init()
    {
        printf("CChild::Init()\n");
    }
};

void use_dynamic_cast()
{
    CParent *pParent = new CParent;
    CChild *pChild = new CChild;
    pParent->Init();    // call CParent::Init()
    pChild->Init();     // call CChild::Init()
    pParent = dynamic_cast<CParent *> (pChild);
    pParent->Init();    // call CChild::Init() from pParent
}

void use_static_cast()
{
    CParent *pParent = new CParent;
    CChild *pChild = new CChild;
    pParent->Init();    // call CParent::Init()
    pChild->Init();     // call CChild::Init()
    pChild = static_cast<CChild *> (pParent);
    pChild->Init();     // call CParent::Init() from pChild
}


I was wondering, are there any other interesting things that can be done by using dynamic_cast and static_cast? -noix-

Share this post


Link to post
Share on other sites
Quote:
Original post by noixtirdoe
pParent = dynamic_cast<CParent *> (pChild);
pParent->Init(); // call CChild::Init() from pParent

That's kind of a waste of time. It's equivalent to: pChild->CParent::Init(); except that really, really braindead compilers might do the latter version more efficiently.

Share this post


Link to post
Share on other sites

void use_dynamic_cast()
{
CParent *pParent = new CParent;
CChild *pChild = new CChild;
pParent->Init(); // call CParent::Init()
pChild->Init(); // call CChild::Init()
pParent = dynamic_cast<CParent *> (pChild);
pParent->Init(); // call CChild::Init() from pParent
}




Output:
CParent::Init()
CChild::Init()
CChild::Init()


void use_dynamic_cast()
{
CParent *pParent = new CParent;
CChild *pChild = new CChild;
pParent->Init(); // call CParent::Init()
pChild->Init(); // call CChild::Init()
pChild->CParent::Init();
}




Output:
CParent::Init()
CChild::Init()
CParent::Init()

Your version doesnt achieve the same effect mine does.

-noix-

EDIT:
However, the use of static_cast to get a pointer back to the parent class is unneeded because then your trick of pChild->CParent::Init() works instead.

Share this post


Link to post
Share on other sites
Quote:
Original post by noixtirdoe
*** Source Snippet Removed ***

Output:
CParent::Init()
CChild::Init()
CChild::Init()

-noix-


Well you don't need to use any cast to assign a child to a parent pointer/reference to achieve that, also 2 phase construction is bad thing what i mean by that is having a constructor that does half or no intialization then a init method to finish it.

Share this post


Link to post
Share on other sites
UH-OH!!

why you use classes and inheritance at all when you want to call functions that way.

Also what you do with your
pChild = static_cast<CChild *> (pParent);
is totally go around that little bit type security c++ brings to you.

only use static_cast if you KNOW 100% the object is the type you cast it to.

in your example of static cast i would expect behavior of the program will go insane if you define a second function to the CChild class. you MAY have luck and all goes like you want, but reordering the functions can already be the thing that brings your system out of balance...

Share this post


Link to post
Share on other sites
Here I added a print() function to the CChild class. After I cast a pointer to the parent class (which we already determined was not needed due to pChild->CParent::Init()), would you assume the pChild would try to call the parent class' method of print() only to find it doesnt exist? No, it actually calls CChild::print(). As far as I can tell, doing this doesnt make the program unstable because everything runs perfectly. But like everyone else has said, the call to static_cast isnt needed.


void use_static_cast()
{
CParent *pParent = new CParent;
CChild *pChild = new CChild;
pParent->Init(); // call CParent::Init()
pChild->Init(); // call CChild::Init()
pChild = static_cast<CChild *> (pParent);
pChild->Init(); // call CParent::Init() from pChild
//pChild->CParent::Init();
pChild->print();
}



Share this post


Link to post
Share on other sites
Quote:
Original post by noixtirdoe
As far as I can tell, doing this doesnt make the program unstable because everything runs perfectly. But like everyone else has said, the call to static_cast isnt needed.


You cannot assume that casting from a parent pointer to a child when calling child's methods doesn't make things unstable if you don't know what type of instance the parent pointer is refering to, it could be a sibling of "child" this all really depends on what member function does, any ways thats the whole point of using dynamic_cast to navigate type hierarchies that will give you an indication of navigation failure. static_cast just does blind cast so only use it when your absolutely sure of the type of the instance it refers to.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Quote:
Original post by noixtirdoe
As far as I can tell, doing this doesnt make the program unstable because everything runs perfectly. But like everyone else has said, the call to static_cast isnt needed.


You cannot assume that casting from a parent pointer to a child when calling child's methods doesn't make things unstable if you don't know what type of instance the parent pointer is refering to, it could be a sibling of "child" this all really depends on what member function does, any ways thats the whole point of using dynamic_cast to navigate type hierarchies that will give you an indication of navigation failure.


Okay, that makes better sense, thanks :D

Share this post


Link to post
Share on other sites
There's a nice trick you can do but I can't remember, anyone like to help out.

Basically if you have:[cpp]class parent
{
...
};

class Child1 : public Parent
{
...
};

class Child2 : public Parent
{
...
};[/cpp]Then you have some way from a pointer to a Parent Object, of seeing if it's actually a Child1 object for example. We used macros in the class definition I think and then we'd use it to to the same thingfor every physics body unless it was a player car to alter the physics.

Share this post


Link to post
Share on other sites
pParent = dynamic_cast<CParent *> (pChild);

You don't need an explicit cast there. Just do

pParent = pChild;

The assignment is legal because the parent type only restricts what operations can be invoked on the object. I can't believe this hasn't been said yet.. And what resource is noixtirdoe using to learn C++ to not know this, yet already be learning about dynamic_cast..

Share this post


Link to post
Share on other sites
Quote:
Original post by noixtirdoe
No, it actually calls CChild::print(). As far as I can tell, doing this doesnt make the program unstable because everything runs perfectly. But like everyone else has said, the call to static_cast isnt needed.


CParent *pParent = new CParent;
pChild = static_cast<CChild *> (pParent);
pChild->print();



This is a BAD idea - you are right in Undefined-Behaviour-Land. Casts are unnecessary when you want to convert from child to parent, but they are very much needed when you convert from parent to child.
static_cast doesn't, cannot verify that the parent pointer you gave it really points to a child or not. All it can check is that the parent and child classes are related. It doesn't check the object - that's the job of dynamic_cast.
Here you're "lucky" because your print function doesn't actually rely on any of the child class properties that aren't already present in parent.
But what if print were to call virtual member functions of child that do not exist in parent? The compiler would blindly go access inexistant vtbl entries, happily sending your program on a one-way trip to Segfault-Land. And that's if you're lucky.
If you're unlucky, the member function will write to some child member variable that doesn't exist in parent, therefore writing outside the object, either in memory you do not own, causing your application to Segfault (again, if you're lucky), or will overwrite another object's data (if you aren't). If your object is on the stack, you may even end up wiping out your stack frame, which is never a good thing. If the data you're writing comes from user input, you're offering crackers a gigantic security hole on a silver platter for them to exploit.

Here's an example:
class Parent
{
protected:
long scalar;
public:
virtual ~Parent() {}
};

class Child : public Parent
{
long array[100];
public:
void reset()
{
scalar = 42;
for(size_t i=0; i<100; ++i)
array[i] = 0;
}
};


Parent *pParent = new Parent[200];
Child* pChild = static_cast<Child *> (pParent);
pChild->reset();
delete[] *pParent;






Sure enough, as Child::reset isn't virtual, the compiler will rely on the object's static type (hey, why do you think it's called static_cast?) to resolve the function call, and will happily call the function, so far so good.
However, it's not a Child object we have, so there really isn't any array array... The reset function will be overwriting the other Parent objects in the array. Their scalar variable will be overwritten, which might be catastrophic in and of itself (imagine it were bank account information - data corruption errors are much, much more severe than crash bugs) but also, in many C++ implementations, the virtual function table pointer (Parent::~Parent() is virtual, remember).
As many people who blindly call zeroing functions (like memset, memzero or ZeroMemory) on their objects, a NULL vtbl pointer is a source of headaches, what with all your virtual function calls crashing your program and all that. Oh, and guess what, our destructor is one such virtual member function - oops. When you try and destroy your object, boom, null pointer, application crash.

So not only did you corrupt your program's data, but you are causing a crash in an unrelated location (cleanup code). Good luck debugging that (well, actually, now that you know, this is behaviour you should be able to recognize on your own - the hard part is to figure out where the offending code is).

So, to conclude, downcasting with a static_cast is not a good idea unless you are absolutely 100% sure the object you are casting really is of the type you are casting to, or of a derived type - i.e. if the object were Child or derived from Child then casting a Parent* to a Child* with static_cast would be OK.

In all other cases, the only safe thing to do is to use dynamic_cast and check whether the pointer it returns is null - meaning the object really isn't of the right type, or not - in which case you can safely proceed.

[Edited by - Fruny on August 19, 2004 2:01:19 PM]

Share this post


Link to post
Share on other sites

This topic is 4866 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this