18

I'm keen to know exactly how the classes will be arranged in memory esp. with inheritance and virtual functions.

I know that this is not defined by the c++ language standard. However, is there any easy way to find out how your specific compiler will implement these say by writing some test code?

EDIT:- Using some of the answers below :-

#include <iostream>

using namespace std;

class A {
  public:
    int a;
    virtual void func() {}
};

class B : public A {
  public:
    int b;
    virtual void func() {}
};

class C {
  public:
    int c;
    virtual void func() {}
};

class D : public A, public C {
  public:
    int d;
    virtual void func() {}
};

class E : public C, public A {
  public:
    int e;
    virtual void func() {}
};

class F : public A {
  public:
    int f;
    virtual void func() {}
};

class G : public B, public F {
  public:
    int g;
    virtual void func() {}
};

int main() {
  A a; B b; C c; D d; E e; F f; G g;
  cout<<"A: "<<(size_t)&a.a-(size_t)&a<<"\n";
  cout<<"B: "<<(size_t)&b.a-(size_t)&b<<" "<<(size_t)&b.b-(size_t)&b<<"\n";
  cout<<"C: "<<(size_t)&c.c-(size_t)&c<<"\n";
  cout<<"D: "<<(size_t)&d.a-(size_t)&d<<" "<<(size_t)&d.c-(size_t)&d<<" "<<(size_t)&d.d-    (size_t)&d<<"\n";
  cout<<"E: "<<(size_t)&e.a-(size_t)&e<<" "<<(size_t)&e.c-(size_t)&e<<" "<<(size_t)&e.e-    (size_t)&e<<"\n";
  cout<<"F: "<<(size_t)&f.a-(size_t)&f<<" "<<(size_t)&f.f-(size_t)&f<<"\n";
  cout<<"G: "<<(size_t)&g.B::a-(size_t)&g<<" "<<(size_t)&g.F::a-(size_t)&g<<" "    <<(size_t)&g.b-(size_t)&g<<" "<<(size_t)&g.f-(size_t)&g<<" "<<(size_t)&g.g-(size_t)&g<<"\n";
}

And the output is :-

A: 8
B: 8 12
C: 8
D: 8 24 28
E: 24 8 28
F: 8 12
G: 8 24 12 28 32

So all classes have got a v-ptr at loc 0 of size 8. D has another v-ptr at location 16. Similarly for E. G also seems to have a v-ptr at 16 although from my (limited) understanding I would have guessed it to have more.

owagh
  • 3,428
  • 2
  • 31
  • 53

5 Answers5

21

One way is to print out the offsets of all the members:

class Parent{
public:
    int a;
    int b;

    virtual void foo(){
        cout << "parent" << endl;
    }
};

class Child : public Parent{
public:
    int c;
    int d;

    virtual void foo(){
        cout << "child" << endl;
    }
};

int main(){

    Parent p;
    Child c;

    p.foo();
    c.foo();

    cout << "Parent Offset a = " << (size_t)&p.a - (size_t)&p << endl;
    cout << "Parent Offset b = " << (size_t)&p.b - (size_t)&p << endl;

    cout << "Child Offset a = " << (size_t)&c.a - (size_t)&c << endl;
    cout << "Child Offset b = " << (size_t)&c.b - (size_t)&c << endl;
    cout << "Child Offset c = " << (size_t)&c.c - (size_t)&c << endl;
    cout << "Child Offset d = " << (size_t)&c.d - (size_t)&c << endl;

    system("pause");
}

Output:

parent
child
Parent Offset a = 8
Parent Offset b = 12
Child Offset a = 8
Child Offset b = 12
Child Offset c = 16
Child Offset d = 20

So you can see all the offsets here. You'll notice that there's nothing at offset 0, as that is presumably where the pointer to the vtable goes.

Also notice that the inherited members have the same offsets in both Child and Parent.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 1
    +1 This example code is closest to what I came up with. Just try to avoid relying on the memory layout. There's no guarantee that it will stay the same in future versions of your compiler (or even in other contexts with the same compiler version, e.g. optimization). I bet there's almost always a better way to solve the problem. – Andre Dec 29 '11 at 19:26
  • Thanks. That does help a bit. Combining your answer with Azza's... I was also interested in what would happen if we have class A; class B; class C : public A, public B; This seems to give a result where there are multiple vtable pointers . The data members of A seem to come first before B. – owagh Dec 29 '11 at 19:38
  • I've never dealt with multiple inheritance. But you can still try it see what the offsets show. I honestly have no idea how multiple inheritance works underneath. – Mysticial Dec 29 '11 at 19:40
  • Edited my question to have a few examples of multiple inheritance. I don't see any v-table pointers though. Probably gcc treats them different from msvcc. – owagh Dec 29 '11 at 20:02
  • Oh pretty! You need to have virtual methods to get a vtable. – Mysticial Dec 29 '11 at 20:05
  • 1
    @owagh I think, you need to get some basic idea about possible different _implementations_ of virtual methods/inheritance, before exploring them in memory. Different implementations will put vptr in different places in object, might (rare implementations) put pointers to virtual base classes into object itself, and organize vtable itself differently. Usually, a single vptr is put in front of the object, it points to vtable which at positive offsets has addresses of virtual functions and at NEGATIVE offsets has _virtual base class offsets_. – lapk Dec 29 '11 at 20:20
  • By `single vptr` I mean `single vptr for each of the virtual subobjects` – lapk Dec 29 '11 at 20:27
  • @Mysticial great response. – ColinCren Feb 06 '14 at 18:04
10

Visual Studio atleast has a hidden compiler option /d1reportSingleClassLayout (starting at ~32:00).

Usage: /d1reportSingleClassLayoutCLASSNAME where there shall be no whitespace between the compiler switch and CLASSNAME (obviously replace this with the name of the class you'e interested in).

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • Hey this is great and exactly what I wanted. Are there similar features for other compilers like gcc, icc and so on? – owagh Dec 29 '11 at 19:21
  • @owagh: Sorry, that I don't know. :/ – Xeo Dec 29 '11 at 19:24
  • @owagh For gcc, this doesn't seem to be a perfect equivalent but maybe it can still be useful: http://stackoverflow.com/questions/15951597/what-is-the-linux-aquivalent-to-msvcs-option-d1reportsingleclasslayout (short: compile in Debug mode with `-g` or `-ggdb`, then use the `pahole` utility on the object file). – gx_ Aug 26 '13 at 12:00
1

Create an object of class, cast pointer to it to your machine's word, use sizeof to find the size of the object, and examine the memory at the location. Something like this:

#include <iostream>

class A
{
 public:
  unsigned long long int mData;
  A() :
   mData( 1 )
  {
  }      
  virtual ~A()
  {
  }
};
class B : public A
{
 public:
  unsigned long long int mData1;
  B() :
   A(), mData1( 2 )
  {
  }
};

int main( void )
{
 B lB;

 unsigned long long int * pB = ( unsigned long long int * )( &lB );

 for( int i = 0; i < sizeof(B) / 8; i++ )
 {
  std::cout << *( pB + i ) << std::endl;
 }

 return ( 0 );
}


Program output (MSVC++ x86-64):

5358814688 // vptr
1          // A::mData
2          // B::mData1

On a side note, Stanley B. Lippman has excellent book "Inside the C++ Object Model".

lapk
  • 3,838
  • 1
  • 23
  • 28
0

The best way is probably writing a few simple test cases and then compile and debug them in assembler (all optimization off): running one instruction at a time you'll see where everything fits.

At least that's the way I learned it.

And if you find any case particularly challenging, post in in SO!

rodrigo
  • 94,151
  • 12
  • 143
  • 190
0

As long as you stick to single inheritance, the subobjects are typically layed out in the order they are declared. A pointer is prepended to they type information at the front which is e.g. used for dynamic dispatch. Once multiple inheritance is incolved things become more complex, especially when virtual inheritance is involved.

To find precise information at least for one ABI flavour you can look for the Itanium ABI. This documents all these details. It is used as the C++ ABI at least on some Linux platforms (i.e. there multiple compilers can produce object files linked into one executable).

To determine the layout just print the addresses of subobjects of a give object. That said, unless you happen to implement a compiler it typically doesn't matter. The only real use of the object layout I doubd is arranging members to minimize padding.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Surely no _vptr_ will be added if there are no `virtual` functions? I'm trying to get a conclusive answer on whether single inheritance of PODs, without anything `virtual`, will result in an exact order of members, i.e. `base.m1, base.m2, derived.m1, derived.m2`. I know I can just try it and see whether it works on my implementation, but I'm really trying to find a guaranteed cross-platform way to do this. (The background is that I'm mapping to binary formats where order is critical.) – underscore_d Dec 29 '15 at 11:44