虚拟类继承引起的对象大小问题

3

在这段代码中,ob1的大小为16,这是正常的(因为虚指针),但我不明白为什么ob2的大小为24。

#include <iostream>
using namespace std;
class A {
    int x;
};
class B {
    int y, z;
};
class C : virtual public A {
    int a;
};
class D : virtual public B {
    int b;
};
int main() {
    C ob1;
    D ob2;
    cout << sizeof(ob1) << sizeof(ob2) << "\n";
}

我原以为ob2的大小是20,但实际输出的大小是24


1
“virtual” 机制需要一些开销。一些编译器将这个开销放入结构中。 - Thomas Matthews
1
通常在虚拟继承中会引入_vtable指针。这可能解释了大小上的差异。 - πάντα ῥεῖ
1
@πάνταῥεῖ: "通常会有一个vtable指针"。虚继承是否引入了vtable指针?我不相信! - Klaus
对于这样的问题,通常最好从打印各种相关标量类型的大小开始,以便我们了解您的平台和C++实现中基本类型的大小,这在流行的实现之间可能会有很大差异。这些可以从复合类型的大小猜测出来,但最好不要这样做。 - curiousguy
@ThomasMatthews "一些编译器将这种开销放在结构中。 " 只是一些?有哪些不放置? - curiousguy
显示剩余12条评论
2个回答

8

D类型对象的一种可能的布局如下:

+----------+
| y        |   The B subobject (8 bytes)
| z        |
+----------+
| vptr     |   vtable pointer (8 bytes)
|          |
+----------+
| b        |   4 bytes
+----------+
| unused   |   4 bytes (padding for alignment purposes)
+----------+

这将使sizeof(ob2)为24。

对齐要求由实现定义。大多数情况下,最大成员对象或子对象的大小决定了对象的对齐要求。在您的情况下,最大对象——vtable指针的大小为8字节。因此,实现会在8位边界上对齐对象,并在必要时添加填充。


1
显然是正确的答案。也许您可以详细说明一下“用于对齐目的”的部分? - Walter
1
我对这个问题很感兴趣:为什么在这种情况下我们需要一个运行时vtable指针。我无法理解为什么它在这里依赖于运行时,因为继承层次结构在编译时完全已知。谢谢...也许还有另一个问题 :-) - Klaus
@Klaus,是的,那将是另一个问题。 - R Sahu
@Klaus 继承关系在编译时并不比函数成员更为明确。虚拟内容可以在派生类中被覆盖,而非虚拟内容则不能。 - curiousguy

2
为了实现虚继承,D作为数据成员包含一个指针,在64位系统上需要8个字节。此外,这8个字节必须对齐到8字节的内存边界。这后一个要求又要求D本身对齐到8字节的内存边界。最简单的实现方法是通过填充未使用的字节(21-24)使sizeof(D)成为8的倍数。"最初的回答"

“_padding it with unused bytes_”这个(几乎可以确定正确的)假设可以通过在末尾添加一个虚拟数据成员,用假定的填充大小来检查总大小没有改变来验证。 - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接