While going through this C++ FAQ https://isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c I encountered the statement
Most C++ compilers use a binary object layout that causes this conversion to happen with multiple inheritance and/or virtual inheritance.
I could not understand the meaning and application of it. As per the C++ FAQ this object layout mechanism helps C++ compiler in the below mentioned check
In C++ it is easy to check if a Derived* called dp points to the same object as is pointed to by a Base* called bp: just say if (dp == bp) .... The C++ compiler automatically converts both pointers to the same type, in this case to Base*, then compares them. Depending on the C++ compiler’s implementation details, this conversion sometimes changes the bits of a pointer’s value.
Could any one help to understand the binary object layout of any popular C++ compilers and what possible changes and the corresponding mechanism for changes in the bits of a pointer’s value. and How it helps in comparing the pointers of Base/Derived classes.
Edit : Please explain why the below is also a valid statement.
NOTE: you must be especially careful when converting both to void* since that conversion will not allow either the C or C++ compiler to do the proper pointer adjustments! The comparison (x == y) might be false even if (b == d) is true:
In this context, "binary object layout" means "how the binary data comprising the object is layed out in memory."
Consider this C++ code:
struct Left
{
int ll;
};
struct Right
{
int rr;
};
struct Derived : Left, Right
{
int dd;
};
One possible way to organise these in memory (conceptually) is as follows:
+ Derived ----------------+
| + Left + + Right + |
| | ll | | rr | dd |
| +------+ +-------+ |
+-------------------------+
Of course, the class is just 3 int
s, so with the above conceptual layout, the real layout would be just this:
+ Derived------+
| ll | rr | dd |
+--------------+
Now imagine this code:
Derived d;
Dervied *pd = &d;
Left *pl = &d;
Right *pr = &d;
pd
points to the start of d
, which is the same as the start of its ll
member.
pl
will point at the Left
subobject of d
. The start of Left
is the start of the ll
member. When comparing pl == pd
, pd
needs to be converted to type Left*
. Remember that pd
already points to the start of ll
, so no change of the value of pd
needs to happen. It's purely a conceptual conversion (a change of type).
pr
points to the Right
subobject of d
. Since a Right
object starts with the rr
member, pr
points to rr
. Again, doing pr == pd
requires pd
to be converted to type Right*
. The Right
subobject of d
starts with the rr
member, but pd
points to the address of the ll
member. Threfore, the value (= bits) of pd
must be changed by this conversion (incremented by the size of one int
) to point to rr
instead. Actually, exactly this conversion already happened once when converting &d
from Derived*
to Right*
in order to initialise pr
with it.
This should also explain why comparing in void*
type doesn't work. Clearly, &d.ll != &d.rr
, even though pl == pr
.
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