Hello All,

i have a small question regarding casts in C++.

1) Now, dynamic_casts ensure safety with casts. ie. for example: for the below polymorphic class:

class Base {   
  public:
   virtual void foo() {}
};

class Derived : public Base {

};

int main()
{
   Base *bPtr = new Base;
   Derived *dPtr = dynamic_cast<Derived*>(bPtr);

   return 0;
}

Now, dPtr would be NULL, thus we can ensure safety via some NULL checks. This is fine.

2) Now, for a non-polymorphic class, suppose i do:

class Base {  
      ...
};

class Derived : public Base {        
       ...
};

int main()
{
   Base *bPtr = new Base;
   Derived *dPtr = static_cast<Derived*>(bPtr);       

   return 0;
}

Now, there is no way to esure safety, and this is a potential unsafe operation.(if the pointer tries to access data from Derived)

Question:
In case of polymorphic classes, we have the safety provided by dynamic_cast. But, in the above non-polymorphic class, there is no safety.

I feel i am missing something here. Why is there no way in C++ to ensure the safety in this case?
Does it mean this above use case is flawed and that an inheritance heirarachy always needs an overriding behavior via virtual methods?

So, maybe i have some misconceptions about polymorphic behavior. Can someone please point out what i'm missing

Thanks!

Now, there is no way to esure safety, and this is a potential unsafe operation.(if the pointer tries to access data from Derived)

It's completely safe...because it won't compile. ;)

I feel i am missing something here. Why is there no way in C++ to ensure the safety in this case?

The entire concept is inherently unsafe. Why are you trying to downcast a pointer to non-polymorphic type?

The idea of polymorphism is that you have a base class reference that may or may not have a "real" type of a derived class. You can call derived class overloads through that reference without having to know what the "real" type is. Provided you only use the interface provided by the base class, you don't need any kind of special provisions for derived class objects.

If the classes aren't polymorphic then it doesn't matter what the "real" type is. You'll be slicing that object through a base class reference in all cases. No magic happens to keep you from corrupting derived class objects by pretending that they're base class objects.

commented: thanks! +5

It's completely safe...because it won't compile. ;)

Using dynamic_cast won't compile. Using static_cast will compile, but trying to access a redefined member (e.g., A has foo() and B has foo() as well) will give you a nice segmentation fault. So, I suppose that is what he meant by "ensuring safety".

Just for the sake of the argument... Your father has a football ball amd you just bought one as well without your father knowledge. When your friend is coming and asks you to lend him a football ball, which one do you give? How would you know which one he is referring to? By default, you would lend the ball you bought, correct? This happens when:

Derived* dPtr = new Derived;
dPtr->foo();

Now let's consider that your friend prefers your father's football ball and he asks your father for your football ball. Your friend request is translated here as

Derived* dPtr = new Derived;
// he gives your friend what he knows
Base* bPtr = dynamic_cast<Base*>(dPtr);
bPtr->foo();

What happens if your friend is asking you and you ask your father for your new football ball? You know you have a football ball, but your father would be caught by surprised:

Base* bPtr = new Base;
// you ask your father to give your football ball
// you know you have it, but you don't realize your
// father has no idea
Derived* dPtr = static_cast<Derived*>(bPtr);
dPtr->foo(); // where in the world that ball can be?

But if your father gave you the money for the football ball, then this translates in polymorphic class:

class Base
{
    ...
    // your father gives you the money,
    // so he expects you to have the football ball
    // therefore if you bought it, that ball will be used
    // otherwise he will give his football ball
    virtual void foo();
}

Thus, he has knowledge of your football ball, so, he won't be caught by surprise.

So, to come to the point, try to see what your are asking from whom.

commented: thanks! +5

Using dynamic_cast won't compile. Using static_cast will compile

Ah, I misread it as dynamic_cast. My bad.

Why is there no way in C++ to ensure the safety in this case?

Why? Because C++ has an overarching principle of no overhead for what you don't use. In order to be able to do the run-time check that dynamic_cast does, the compiler has to add a hidden virtual function (or set of virtual functions) to perform this dynamically-dispatched cast operation. A virtual function requires a virtual table (per class) and a virtual table pointer (per object), and that's overhead. If the class already has at least one virtual function, that overhead is already present, and so, adding another hidden virtual function to enable the dynamic cast does not represent any overhead (per object). And that's "why" things are like they are. And also because if the compiler must put in place the facilities required to support dynamic casts on non-polymorphic classes, it must put those facilities in place for all classes (including C-style structs, which would break compatibility with C), and that would be unacceptable in terms of overall overhead (unacceptable by C++ standards, not by other people's standards, as in Java/C#).

Does it mean this above use case is flawed and that an inheritance hierarchy always needs an overriding behavior via virtual methods?

It depends what your intent is by "inheritance hierarchy". Inheritance can serve a few different purposes (and a few more purposes at the "expert level"). The main purpose of inheritance is to allow for dynamic polymorphism, and that requires a dynamic dispatch mechanism. Dynamic dispatching means that there is a way (or pathway) to go from a placeholder to an actual implementation (that's the "dispatch" part), and that this pathway is created at run-time (that's the "dynamic" part). A virtual function is such a dynamic dispatch mechanism (the function name is the placeholder, and the virtual table pointer and function pointer is the pathway), and it is the native mechanism in C++ (and most other languages that support OOP). So, if your purpose is to use inheritance in order to use dynamic polymorphism in any way, then you're gonna need at least one virtual function.

Doing a dynamic cast to gain access to the full interface (public member functions and data members) of a derived class with relation to a given object (pointee) is a big deal and not something to be done lightly. You can access key aspects of the derived class via its base class' virtual functions. If that's not enough, then consider redesigning your hierarchy and/or that set of virtual functions. Use a dynamic cast if that's still not enough, e.g., the multi-dispatch problem is one legitimate case, and there are also legitimate cases for external polymorphism (i.e., external derived-class implementations of some functionality, to avoid clogging the base class or isolating an external dependency). The important point here is that a dynamic cast is a kind of hardcore and blunt method for polymorphism, and it is to be used as a last resort.

So, this idea that you would ever want to use a dynamic cast on a non-polymorphic class hierarchy is very unusual. If you are willing to go as far as a dynamic cast, then the price of requiring a virtual function (e.g., making the base-class destructor virtual) is one that you would normally pay gladly. Do you really have a real use-case in which you need to use a dynamic down-cast and cannot suffer the presence of a virtual function?

I feel i am missing something here.

Yes, you are. There are a few reasons for inheriting from a class while not having any dynamic polymorphism involved (virtual functions). It's usually not a great idea to do so, but there are some common cases. In any case, you can't really use such an object as you would with a polymorphic object. And you certainly cannot intend people to use that class hierarchy as if it was meant to be polymorphic. For instance, the owner of the object has to always keep track of its actual type (derived class), you are not allowed to "lose" that information, because that information is required when deleting the object (or letting it go out of scope) since the base-class destructor is not virtual, meaning that deleting the object via a pointer of any other type than the actual (most-derived) type of the object would be a bug. Also, you can't use a base-class pointer to access anything from the derived class, since the dynamic dispatching pathway is closed (no virtual table). And with those limitations in mind, it simply doesn't make any practical sense to attempt a dynamic cast. And, of course, you can't, since a dynamic cast won't compile, but a static cast will, but won't be safe at run-time.

Most of the legitimate cases for inheritance without polymorphism are cases where you facilitate the implementation of some set of (derived) classes by having them all inherit from some base class with some common functionalities. But often that inheritance should be private. And, in those cases, this "implementation-helping" base class is not meant to be exposed by the library (it's somewhat hidden), nor be used polymorphically.

But, if you do need to make a static down-cast (from base to derived) safely, then you have to implement some other mechanism to make sure the cast is correct. One way is to have a protected data member in the base class that identifies the derived class. But at this point, we're talking about somewhat "hacky" code that really should be implemented with the preferred mechanisms of dynamic polymorphism via virtual functions (at the very least, just make the destructor virtual and use dynamic-cast). And, the overhead of this hacky method is the same or more than a virtual function, and you still need to modify the base-class to enable this polymorphic use of the objects (i.e., it is an intrusive method). So, you get lots of pain but no gain. What's the point?

commented: excellent explanation! +5

@CGSMCMLXXV

So, I suppose that is what he meant by "ensuring safety".

Thanks for the reply! Yes, that is precisely what i meant.

@deceptikon
Thanks for the reply!

@mike_2000_17
Thanks for the wonderful explanation!

Do you really have a real use-case in which you need to use a dynamic down-cast and cannot suffer the presence of a virtual function?

Not really. I just thought of this use case. It's not a real use-case. Now i understand inheritance better.

Most of the legitimate cases for inheritance without polymorphism are cases where you facilitate the implementation of some set of (derived) classes by having them all inherit from some base class with some common functionalities. But often that inheritance should be private. And, in those cases, this "implementation-helping" base class is not meant to be exposed by the library (it's somewhat hidden), nor be used polymorphically.

Could you please provide an example? I didn't quite get you.
Thanks a lot!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.