Suppose the following code with basic structures
struct A {int aMember};
struct B {bool bMember};
struct C {double cMember};
struct BA : B, A {};
struct CB : C, B {} ;
void test(B *base) {
bool retrieved = base->bMember;
}
Here the test function is able to be passed a pointer to an instance of B, BA, or CB. How is the retrieval of the base class member "bMember" achieved in low-level terms? Presumably the member can't be guaranteed to be located at a given offset from the passed objects addressee for every derived type. In short, how is it "known" where the slice of B's members are located for any given derived type? Is this achieved at run-time with some sort of meta-data associated with objects and classes utilizing inheritance?
I'm terribly sorry if there's a simple explanation already posted. I just didn't know how to phrase my search to return a relevant answer.
Thankyou!
A given member of B
is always at the same offset from base
, because base
is a B*
.
The piece of the puzzle I think you're missing is that test
isn't passed the address of the object itself when you're passing a BA*
or a CB*
.
Instead, it's passed the address of their respective subobjects of type B
.
Example using your classes:
void test(B *base) {
std::cout << "test got " << base << std::endl;
}
int main()
{
BA ba;
CB cb;
std::cout << "&ba: " << &ba << std::endl;
std::cout << "ba's B subobject: " << static_cast<B*>(&ba) << std::endl;
test(&ba);
std::cout << "&cb: " << &cb << std::endl;
std::cout << "cb's B subobject: " << static_cast<B*>(&cb) << std::endl;
test(&cb);
}
For me, this printed
&ba: 0x28cc78
ba's B subobject: 0x28cc78
test got 0x28cc78
&cb: 0x28cc68
cb's B subobject: 0x28cc70
test got 0x28cc70
Both calls to test
pass the B
subobject to the function, and every B
object looks the same, so test
doesn't need to care about the other classes at all.
Note that ba
and ba
's B
subobject are in the same place; this particular implementation arranges subobject in the order they're specified in the inheritance list, and the first one is located first in the derived object.
(This isn't mandated by the standard, but it's a very common layout.)