Why does static_cast provide different memory locations for the same object?
My code
class Parent { int a; };
class Child { int b; };
struct GrandChild : public Parent, public Child { int a, b, c; };
int main() {
GrandChild GC;
std::cout << "GrandChild address is at : " <<&GC<<endl;
std::cout << "Child address is at : " <<static_cast<Child*>(&GC)<<endl;
std::cout << "Parent address is at : " <<static_cast<Parent*>(&GC)<<endl;
}
output:
GrandChild address is at : 0077F6F8
Child address is at : 0077F6FC
Parent address is at : 0077F6F8
Why after static_cast memory cells have inconsistencies as above?
source to share
GrandChild
comes from Parent
and Child
. Thus, an object GrandChild
in memory consists of an object Parent
object Child
in its memory.
&GC
by itself returns the address of the GrandChild
object GrandChild
as a whole
static_cast<Parent*>(&GC)
returns the starting address of the Parent
part inside the object GrandChild
.
static_cast<Child*>(&GC)
returns the starting address of the Child
part inside the GrandChild
object.
In your case Grandchild
, inherits from first Parent
, so the Parent
part is aligned at the beginning of GrandChild
memory GrandChild
. Then Child
part follows Parent
part. Here is an illustration to show that:
source to share
&GC
is the address of the object GrandChild
GC
. static_cast<Child*>(&GC)
is the address of the subject Child
GC
. And static_cast<Parent*>(&GC)
- this is the address of the subject Parent
GC
.
In your specific implementation, it seems that the object GrandChild
starts with a subobject Parent
and then the subobject Child
comes after, so the subobject's address is the Parent
same as the full object's GrandChild
address, but the first byte subobject is Child
not the first byte of the full object GrandChild
, so its address is higher. However, you cannot rely on portable behavior; different implementations can allocate base class and member subobjects in different orders and do not even have to be compatible between different classes.
source to share
In your example, this is predictable even though it's implementation defined (but developers are allowed to choose straight forward solutions for simple cases :-))
Here's a memory representation of your object (derived from addresses, not the standard!):
Parent::a (int = 4 bytes)
Child::b (int = 4 bytes)
GrandChild::a
GrandChild::b
GrandChild::c
This is because of your declaration: GrandChild
inherits from first Parent
, then from Child
. With this representation, it makes sense that the address Parent
will be the same as GrandChild
one, but for Child
"address, there will be 4 more.
Also note that GrandChild::a
do not Parent::a
...
source to share
It looks like it depends on the sequence in which GrandChild is inherited from parent and child.
In the following sequence, I received
struct GrandChild : public Parent, public Child { int a, b, c; };
Output 1: GrandChilds address matches parent's
GrandChild address is at : 0x22fecc
Child address is at : 0x22fed0
Parent address is at : 0x22fecc
When changing the sequence in which GrandChild inherits from Child and Parent to
struct GrandChild : public Child , public Parent { int a, b, c; };
Output 2: GrandChilds address is the same as Child's
GrandChild address is at : 0x22fecc
Child address is at : 0x22fecc
Parent address is at : 0x22fed0
source to share
The other answers here have worked great, but let me worry anyway.
First, the parent is not the parent, and the child is not the child ... of the parent; how it did not inherit from the parent. Both Parent and Child are legacy from GrandParent, which is not ... the grandparent!
Second, to answer your question, the effects you are observing are not really inconsistencies, but how C ++ implements polymorphism. (I don't think the other answers have made this clear).
PolyMorphism (poly = many, morphism = morphing into) is an object-oriented programming concept that allows one object to turn into many different objects at runtime. This allows the object to behave differently. For example, now it might be a dog, then a cat, and later a ghost.
How polymorphism is implemented in most object-oriented languages, C ++, Java, etc. Through pointer arithmetic (increment) and inheritance. It would be great if std :: string could transform to std :: vector, but since they do not share inheritance, this is not syntactically possible.
However, in the case of your parent class, it can become GrandParent or any other derived class. Likewise, GrandParent can turn into Parent or Child.
ANOTHER NAME OF POLYMORPHISM IS POLYMORPHIC CONVERSION !!!
Based on your question, it is important to understand that polymorphism is a type of C ++ conversion. The C ++ conversion is designed to be accurate and lossless. For example, you can convert int to char and vice versa. Data integrity is maintained! Likewise, if I convert from GrandParent to Child (which is what static_cast does); Your best bet is to set the object pointer to the Child address. If we convert to child and continue reading from GrandParent, we will read WRONG DATA. In your case, when you inherited first from Parent, we will end up reading the a and b values ββstored in Parent.
Even worse, it becomes more obvious that such a treatment would be wrong. If we are converting to Child, and child has a special getString function, for example, if we call this function from the start address of the GrandChild, BOOM! We will definitely get a crash at runtime!
I hope you enjoyed this video and learned a thing or two. If you want more free videos, don't forget to subscribe. Thank.
source to share