继承类的内存布局

时间:2011-12-29 19:04:26

标签: c++ inheritance layout

我很想知道如何将这些类安排在内存中。具有继承和虚函数。

我知道这不是由c ++语言标准定义的。但是,是否有任何简单的方法可以通过编写一些测试代码来了解您的特定编译器如何实现这些?

编辑: - 使用以下一些答案: -

#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";
}

输出结果为: -

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

所以所有类都有一个大小为8的loc 0的v-ptr。 D在位置16处有另一个v-ptr。类似于E. G似乎也有16的v-ptr,虽然从我(有限的)理解中我会猜到它有更多。

5 个答案:

答案 0 :(得分:13)

一种方法是打印出所有成员的抵消:

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");
}

<强>输出:

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

所以你可以在这里看到所有的偏移。您会注意到偏移0处没有任何内容,因为大概是指向vtable的指针。

另请注意,继承的成员在Child和Parent中都具有相同的偏移量。

答案 1 :(得分:7)

Visual Studio atleast有一个hidden compiler option /d1reportSingleClassLayout(从~32:00开始)。

用法:/d1reportSingleClassLayoutCLASSNAME编译器开关和CLASSNAME之间不应有空格(显然用你感兴趣的类的名称替换它)。

答案 2 :(得分:1)

创建一个类的对象,将指针指向它的机器的单词,使用sizeof查找对象的大小,并检查该位置的内存。像这样:

#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

另一方面,Stanley B. Lippman有很好的书"Inside the C++ Object Model"

答案 3 :(得分:0)

最好的方法可能是编写一些简单的测试用例,然后在汇编程序中编译和调试它们(所有优化都关闭):一次运行一条指令,你会看到一切都适合。

至少这是我学习它的方式。

如果您发现任何案件特别具有挑战性,请在SO中发帖!

答案 4 :(得分:0)

只要您坚持单继承,子对象通常按声明的顺序进行布局。它们在前面键入信息前面有一个指针,例如,用于动态调度。一旦涉及多重继承,事情就会变得更加复杂,尤其是在涉及虚拟继承时。

要查找至少一种ABI风格的准确信息,您可以查找Itanium ABI。这记录了所有这些细节。它至少在某些Linux平台上用作C ++ ABI(即多个编译器可以生成链接到一个可执行文件中的目标文件)。

要确定布局,只需打印给定对象的子对象的地址。也就是说,除非您碰巧实现编译器,否则它通常无关紧要。唯一真正使用的对象布局我怀疑是安排成员最小化填充。