Hello, I am currently learning about how virtual is implemented in the Itanium ABI. Here is the layout of a class Bat that derives from the classes Bird and Mammal (in that order):
As you can see, the vptr and data for the Mammal is somewhere in the middle of the Bat object. My question is, in cases of multiple inheritance, how is it determined which subobject of the class Bat (Bird or Mammal) will appear at the beginning of the object and which will appear in the middle? Is it determined by the order it is listed when inheriting from them?
> in cases of multiple inheritance, how is it determined which subobject of the class
> will appear at the beginning of the object and which will appear in the middle?
When multiple inheritance is involved, the class is not of a StandardLayoutType;
the standard has nothing to say about its lay out.
> Is it determined by the order it is listed when inheriting from them?
In most implementations, other things being equal, yes.
Quite often, other things may not be equal. For example:
#include <iostream>
// caveat: this macro is not portable
#define OFFSET_OF_BASE( derived, base ) \
( (std::ptrdiff_t)(const base*)(const derived*)1'000'000 - 1'000'000 )
namespace one
{
struct A { char c[121]; };
struct B { int i ; };
struct C : A, B {} ;
}
namespace two
{
struct A { char c[121]; };
struct B { int i ; virtual ~B() = default ; }; // B has a virtual function
struct C : A, B {} ;
}
int main()
{
{
usingnamespace one ; // sub-object of type A is at an offset of zero
std::cout << OFFSET_OF_BASE(C,A) << ' ' << OFFSET_OF_BASE(C,B) << '\n' ; // 0, 124
}
{
usingnamespace two ; // sub-object of type B is at an offset of zero
std::cout << OFFSET_OF_BASE(C,A) << ' ' << OFFSET_OF_BASE(C,B) << '\n' ; // 12, 0
}
}
#include <iostream>
// caveat: this macro is not portable
#define OFFSET_OF_BASE( derived, base ) \
( (std::ptrdiff_t)(const base*)(const derived*)1'000'000 - 1'000'000 )
struct A { char c[121]; };
struct B { int i ; };
struct C : A, B {} ;
int main()
{
const C c{} ; // consider an object of type C
const C* pc = std::addressof(c) ; // take its address
const B* pb = static_cast<const B*>(pc) ; // get the address of the base class sub-obect
// note that the cast is superfluous here; it is there to mimic the cast in the macro
// get the numeric value of these two pointers
std::intptr_t pc_value = reinterpret_cast<std::intptr_t>(pc) ;
std::intptr_t pb_value = reinterpret_cast<std::intptr_t>(pb) ;
// subtract one numeric value from the other to get the offset in bytes
std::cout << "offset of sub-oject B == " << pb_value - pc_value << '\n'
// same as above, using old-style casts
<< "offset of sub-oject B == "
<< (std::intptr_t)(const B*)(const C*)pc_value - pc_value << '\n'
// the macro works with the supposition of a fictitious object of type derived
// which is at an address which has a numeric value of 1'000'000,
// and for brevity, uses old-style casts
<< " OFFSET_OF_BASE(C,B) == " << OFFSET_OF_BASE(C,B) << '\n' ;
}
I'm coming back to this thread because I had a question regarding your first post. In your first usage of OFFSET_OF_BASE (using namespace one), the sub-object is B and is located at an offset of 124 (which is expected, 120 bytes for the char array and 4 bytes for the int = 124), but the subobject for namespace two is A, located 12 bytes after B (at the beginning of the object).
If I am correct (and please correct me if I'm wrong), all the B object holds is a single integer (usually 4 bytes) and a vptr (8 bytes), thus it equals 12. Does that sound right? In cases of a polymorphic base class and a non-polymorphic class in multiple inheritance, will the polymorphic one always appear first in the derived object, despite the order?
> In cases of a polymorphic base class and a non-polymorphic class in multiple inheritance,
> will the polymorphic one always appear first in the derived object, despite the order?
Nothing is guaranteed by the standard.
However most implementations would favour placing the object of the polymorphic base class at an offset of zero, since it would be a more efficient layout.