I found a few other questions that seem to implicate my question, but none directly answered it (even the one with almost the same name).
I have a C++ child class derived from two parents. I create an object of that class, then cast its reference to each of its two parents. The cast values are not the same, though one remains the value of the child reference. Here's the code:
class Mom
{
public:
int a = 1;
};
class Dad
{
public:
int b = 2;
};
class Child : public Mom, public Dad
{
public:
int c = 3;
};
int main()
{
Child* c = new Child; // 0x00C5A618
Mom* m = c; // 0x00C5A618
Dad* d = c; // 0x00C5A61C
return 0;
}
Now, I am guessing that the addresses obtained by applying static_cast<> are those of the actual subobjects. As it happens, Mom
is the first subobject, so it actually has the same address as Child
. Dad
is the second subobject, so it resides at a different address.
As I understand it, C-style casts, like this
Mom* m = (Mom*)c;
Dad* d = (Dad*)c;
will apply a static_cast<> when it would be legal to do so. Indeed, when I actually compile and execute that version, I see the same behavior as before.
Continuing this scintillating saga, using implicit casts, like this
Mom* m = c;
Dad* d = c;
exhibit the same behavior as well, from which I infer that implicit casts also apply static_cast<> when the equivalent explicit cast would be legal.
Downcasting back to Child
does the same thing:
Child* k = static_cast<Child*>(d); // 0x00C5A618
The value in k
is different from the value in d
, having been set back to the value originally in c
.
I am guessing that, in this case, there is a run-time check to see if, indeed, d
points to a Dad
subobject of a Child
object.
So far, this all makes sense, but I'm surprised that there is no such address adjustment (if that's the right thing to call it) for singly derived hierarchies. That is, if my hierarchy is simply Mom
->Dad
->Child
, casting a reference to a Child
into a pointer to either a Mom
or a Dad
never changes the value:
class Mom
{
public:
int a = 1;
};
class Dad : public Mom
{
public:
int b = 2;
};
class Child : public Dad
{
public:
int c = 3;
};
int main()
{
Child* c = new Child; // 0x00C5A618
Mom* m = c; // 0x00C5A618
Dad* d = c; // 0x00C5A618
Child* k = static_cast<Child*>(d); // 0x00C5A618
return 0;
}
I'd have expected it to change by the size of the the storage needed for each additional subclass, or four bytes (the size of an int). But, they don't. All of the pointers in the above example, c
, m
, d
, and k
, have the same value.
So, now I am guessing that the objects were laid out in memory with the Mom
object at the lowest address, the additional space needed for the Dad
object coming next, and the additional space needed for the Child
coming last. Since a Dad
is-a Mom
, the Dad
address would be the same as the Mom
address, wouldn't it?
Likewise, a Child
is-a Mom
too, so its address would also be the same as its own Mom
subobject's address.
So...
I think it is working like this: in memory, the objects follow each other such that, in purely singly derived hierarchy, upcasting and downcasting always yields the same address, while in a multiply derived hierarchy, some upcasts yield a different address, because not all subobjects will start at the same address as the derived object. Like this:
Possible Memory Layout for a Singly Derived Hierarchy
+-------+-----+-----+---+
| Child : Dad : Mom : a |
| | +-----+---+
| | : b |
| +-----------+---+
| : c |
+-----------------------+
Possible Memory Layout for a Multiply Derived Hierarchy
+-------+-----+---+
| Child : Mom : a |
| +-----+---+
| : Dad : b |
| +-----+---+
| : c |
+-----------------+
Sorry that I've rambled a bit, but my question is this: is the fact that an upcast to some base classes of a multiply derived object changes the value of the reference a result of the fact that the post-cast value is the address of the base object in memory?
A cast does not change the value of an object. While technically correct, your answer is misleading. Casting a pointer to a class instance may return a different pointer address than the casted pointer contains.
Upcasting can cause object slicing when a derived class object is passed by value as a base class object, as in foo(Base derived_obj). Downcasting. The opposite process, converting a base-class pointer (reference) to a derived-class pointer (reference) is called downcasting.
Upcasting is safe casting as compare to downcasting. It allows the public inheritance that implicitly cast the reference from one class to another without an explicit typecast.
C++ allows that a derived class pointer (or reference) to be treated as a base class pointer. This is upcasting. Downcasting is an opposite process, which consists of converting base class pointer (or reference) to derived class pointer.
Yes, you are right. Upcasting a pointer gives the address of the parent subobject which is stored somewhere inside the child class. Usually the parent subobject is stored at the beginning of the child object, so pointers to the child and base subobject are the same.
In case of multiple inheritance, it is impossible for all parents to occupy the same memory (let's ignore possible Empty Base Optimization), so all, but one, base class addresses will have to differ from the child address.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With