3

我试图了解 C++ 在多重继承中的对象布局。为此,我有两个超类 A、B 和一个子类 C。我在尝试转储它时期望看到的是:vfptr | A 领域 | vfptr | B 领域 | C的领域。

我得到了这个模型,但有一些我不明白的零。这是我正在尝试的代码

    #include <iostream>

    using namespace std;

    class A{
       public:
       int a;
       A(){ a = 5; }
       virtual void foo(){ }
    }; 

    class B{
       public:
       int b;    
       B(){ b = 10; }
       virtual void foo2(){ }
    };

    class C : public A, public B{
       public:
       int c;    
       C(){ c = 15; a = 20;}
       virtual void foo2(){ cout << "Heeello!\n";}
    };

   int main()
  {
    C c;
    int *ptr;

ptr = (int *)&c;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;

       return 0;
   }

这是我得到的输出:

4198384     //vfptr
0
20          // value of a
0
4198416     //vfptr
0
10          // value of b
15          // value of c

中间的零是什么意思?提前致谢!

4

6 回答 6

2

如果您正在使用 64 位系统,那么:

  • 第一个零是第一个的 4 个最高有效字节vfptr

  • 第二个零是填充,因此第二个vfptr将与 8 字节地址对齐。

  • 第三个零是第二个的 4 个最高有效字节vfptr

您可以检查是否sizeof(void*) == 8以断言。

于 2014-02-09T20:07:40.733 回答
2

这在很大程度上取决于架构和编译器...可能对您来说指针的大小可能不是 int 的大小...您使用的是什么架构/编译器?

于 2014-02-09T20:07:09.867 回答
2

这取决于你的编译器。使用 clang-500,我得到:

191787296
1
20
0
191787328
1
10
15
1785512560

我确信也有一种 GDB 方式,但是如果我在类对象的地址处使用 LLDB 转储指针大小的字,就会得到这样的结果:

0x7fff5fbff9d0: 0x0000000100002120 vtable for C + 16
0x7fff5fbff9d8: 0x0000000000000014
0x7fff5fbff9e0: 0x0000000100002140 vtable for C + 48
0x7fff5fbff9e8: 0x0000000f0000000a

这种布局看起来很明智,对吧?正是你所期望的。这在您的程序中没有像在调试器中那样显示干净的原因是您正在转储 int 大小的单词。在 64 位系统上 sizeof(int)==4 但 sizeof(void*)==8

因此,您会看到您的指针分成 (int,int) 对。在 Linux 上,您的指针没有设置超出低 32 位的任何位,而在 OSX 上,我的指针设置了 - 因此 0 与 1 差异的原因

于 2014-02-09T20:04:27.480 回答
1

这完全取决于您的编译器、系统和位数。

虚拟表指针将具有指针的大小。这取决于您是将文件编译为 32 位还是 64 位。指针也将在其大小的多个地址处对齐(就像任何类型通常一样)。这可能就是为什么您在20.

整数将具有特定系统上的整数大小。这通常是 32 位的。请注意,如果您的机器上不是这种情况,您将得到意想不到的结果,因为您使用指针算法将 ptr 增加了 sizeof(int) 。

于 2014-02-09T20:10:20.453 回答
1

在不了解您的平台和编译器的情况下很难判断,但这可能是对齐问题。实际上,编译器可能会尝试沿 8 字节边界对齐类数据,并使用零填充。

如果没有上述细节,这只是猜测。

于 2014-02-09T20:03:21.090 回答
1

如果您使用 MVSC,您可以使用 -d1reportAllClassLayout 转储解决方案中所有类的所有内存布局,如下所示:

cl -d1reportAllClassLayout main.cpp

希望对你有帮助

于 2015-06-03T07:23:52.030 回答