polymorphic downcasting for derived classes with additional members - c++

Consider a class Base with virtual functions and a class Derived from Base implementing the virtual functions, but with few additional private members.
Can we safely downcast the Base* pointer to Derived* pointer?
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // Is this valid?
What if derived class contains an additional private member int _derivedNum apart from implementation of virtual functions in the base class? Can I still use derived->_derivedNum to access the private member of the derived class after downcasting the base class pointer?

Yes, you certainly can. The cast is a runtime function, based on runtime type identification. If it fails, it returns a null pointer.

Yes we can , safely downcast the Base* pointer to Derived* pointer.
Base* base = new Derived();
Derived* derived;
//Null is returned, if the cast is not safe
if( (derived = dynamic_cast<Derived *>(base)) != NULL)
{
//cast ok, can call methods of derived class
}

With one minor proviso, yes, that's exactly what dynamic_cast is built for.
The proviso is that your cast needs to be to pointer to Derived instead of just Derived:
Derived* derived = dynamic_cast<Derived *>(base);
But the basic point of dynamic_cast is that it first checks that the pointee object is really of the derived type, and then returns a pointer to it, or returns a null pointer if the pointee object isn't actually of (or derived from) the requested target type.
Also note that for it to work, the base class needs to contain at least one virtual function (though if it doesn't contain any virtual functions, it's probably not intended to be used as a base class anyway. There are exceptions to that (e.g., std::iterator) but they are exceptions, not the general rule.

Related

C++ derived-class members after downcasting

I recently learned about upcasting and downcasting in C++. However I came up with a few questions during reading about downcasting. Say I have two classes
class Base {
public:
virtual void foo() {}
};
class Derived : public Base {
public:
Derived(int i) {
mem = i;
}
int mem;
};
My questions are the followings:
If I create an object Derived d(1), upcast to Base class, and then downcast it back to Derived class, is 'mem==1' preserved? Do I still have access to it? Assume pointer or reference is used so object slicing doesn't happen and dynamic_cast is used for downcasting.
When downcasting from Base class to Derived class, there will an additional member variable 'mem'. Is memory allocated for 'mem' during run-time (using dynamic_cast)? To what value will it be initialized to?
After some simple experiments, 1 seems to be true.
However for 2 it seems I can't start from a Base class pointer and dynamic_cast it into Derived class pointer as dynamic_cast returns null.
I read from another post saying "But the basic point of dynamic_cast is that it first checks that the pointee object is really of the derived type, and then returns a pointer to it, or returns a null pointer if the pointee object isn't actually of (or derived from) the requested target type."
Is this saying we can't actually start from the Base class and simply downcast it into Derived class, but rather the whole point of dynamic_cast is to "cast back" something that has been upcasted?
It depends; if you cast a Derived object to Base, the result is a new object created by omitting the fields that are not in the Base. However, if you cast a pointer to Derived (i.e. Derived*), the result is a pointer which points to the same object but whose type is Base*.
It depends; if you cast a Base object, you get a compile error unless you overload the typecast operator for doing such an operation (you have correctly observed that the values of the fields would otherwise be undefined). However, if you cast a Base* (a pointer) to Derived* (using dynamic_cast<Derived*>(p)), the result depends on the object the pointer points to. If it points to an instance of the Derived class (or its subclass), the result is a Derived* pointer to that object; otherwise, you get a nullptr pointer of type Derived*.

Why don't the two pointer values be the same?

Quoting Effective C++, Scott Meyer, 3rd Edition , Item 27
class Base { ... };
class Derived: public Base { ... };
Derived d;
Base *pb = &d; // implicitly convert Derived* ⇒ Base*
Here we’re just creating a base class pointer to a derived class
object,
but sometimes, the two pointer values will not be the same. When that’s the case, an offset is applied at runtime to the Derived*
pointer to get the correct Base* pointer value.
Why are the two pointer values not the same? If it is because how the child and parent objects are laid out in the memory, then how does downcast work later?
This always happen when using multiple inheritance.
class Base1 { int a; };
class Base2 { double b };
class Derived : public Base1, public Base2 { ... };
Derived d;
Base1* pb1 = &d;
Base2* pb2 = &d;
Now &d cannot possibly be equal to both pb1 and pb2, because otherwise pb1 would equal pb2, which is not possible, because two different non-empty objects of unrelated types must occupy different regions of memory. So in at least one case a non-zero offset must be applied.
In most implementations with single inheritance the offset is zero, but the standard does not mandate that.
Indeed, a typical implementation would simply lay out the base object at the beginning of the derived object:
++-----++
||Base ||
|+-----+|
|Derived|
+-------+
But when there are more than one base, only one can go at the beginning:
++-----++
||Base1||
|+-----+|
||Base2||
|+-----+|
|Derived|
+-------+
The downcast works because the offset is fixed and known at compile time, so there's no problem to apply it when either upcasting or downcasting.
An exception to this is virtual inheritance. For a virtual base the offset is not known at compile time. Typically, the derived object contains an internal hidden pointer to its virtual base, so the upcast can work. But the base doesn't know where its derived object is, so the downcast cannot work, and is not allowed by the language.
When you have a single inheritance tree that is polymorphic, compilers require each object to begin with a VMT (virtual method table). If base class is polymorphic, the pointer values would match. But ff your base class is non-polymorphic and your derived class is polymorphic, the base class does not introduce the VMT, it is introduced by the very first polymorphic class down the tree. The VMT would be inserted before the base. Now the pointer values will not match.

If derived class contains only base class element ,then downcast is possible using dynamic_cast in c++

class Base { virtual void dummy() {} };
class Derived: public Base { // no new elements added }
I like to know that
base *bptr;
derived *dptr;
bptr=new base;
dptr=dynamic_cast<derived*>(bptr); // this is not working ,dptr is NULL
dynamic_cast can also downcast (convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with virtual members) if -and only if- the pointed object is a valid complete object of the target type"
here the pointed object is base object and is same as target object.
so here "complete" means some other meaning
thanks
dynamic_cast will only succeed if the dynamic type of the object matches the type you're casting to. Here
bptr=new base;
points to a base, not a derived, so dynamic_cast<derived*> will fail; there is no derived object it could point to. derived is a different type to base, even if it doesn't declare any members beyond those in the base class.
Try
bptr=new derived;
and the cast should succeed.

If I convert a derived pointer to a base pointer, are the two pointers guaranteed to have the same value?

Assuming that I'm not using multiple inheritance or virtual inheritance, does the C++ standard guarantee that if I convert a Derived* to a Base* that both pointers will have exactly the same value? For instance, will the following code always call success(), even if Derived and/or Base have virtual functions?
Derived* d = new Derived;
Base* b = d;
if (((void*)b) == ((void*)d))
success();
else
failure();
If the base class has no virtual functions and the derived class does, the pointers would likely be different because the base class wouldn't have the vtable pointer present in the D object.

Is there a way to return a child* to a base* and evaluate which child* has been returned?

I tried this which didn't work:
void Map::OnLMClick(short xPos, short yPos)
{
SObject* pSObject = pEditWindow->GetSelectedItem();
if (pSObject==SOTile)
{
/* Do */
I tried this as a test:
SObject* EditorWindow::GetSelectedItem()
{
return pSOTile[1]; //pSOTile[1] is a valid pointer, member of EditorWindow
}
SOTile class is a child of base class SObject. So, is it possible to create a Base* to get one of its child's* returned and then have the program react differently depending of what child it returned? If it is the case, how do I then have access to members of it's child that are not members of base?
If your types are polymorphic (i.e. if they have at least one virtual function), you can use dynamic_cast:
Base* pObject = get_object(); // May return a pointer to Derived
Derived* pDerived = dynamic_cast<Derived*>(pObject);
Notice, that dynamic downcasts are sometimes an indicator of bad design. Try to think if a better arrangement of virtual functions in your hierarchy would help.
Without knowing the concrete application domain, it is not possible for me to give more concrete advices.
dynamic_cast operator performs a special checking when a class is polymorhic.
SOTile* pSOTile = dynamic_cast<SOTile*>(pSObject);
if (pSOTile)
{
/* Do */
}
Since SOTile is a pointer to a type that derives from SObject, you should not need to cast at all, since the compiler should be able to automatically resolve an SObject pointer from the derived pointer and then compare the two SObject pointers together (the same way that you can assign a derived pointer to a base pointer without type-casting). Some compilers, like Borland's, support that just fine, eg:
class Base
{
};
class Derived : public Base
{
};
Derived *d = ...;
Base *b = ...;
if (b == d) // <-- compiles fine
However, if for whatever reason your compiler does not allow that, you can manually cast the derived pointer using static_cast (don't use dynamic_cast to cast the base pointer):
if (pSObject == static_cast<SObject*>(SOTile))
If SOTile is not a pointer to a type that derives from SObject, static_cast will fail at compile-time.
I wouldn't recommend to use dynamic_cast. Instead, you can implement that different behavior in different child class. Sometimes called as "template method" pattern