为什么sizeof(BaseClass)== sizeof(DerivedClass)虽然我添加了一个成员

时间:2014-01-19 18:25:13

标签: c++ oop sizeof

从以下代码sizeof(Base) == 24sizeof(Derived) == 24

为什么他们的尺寸相等?

Base班级,我们有3名成员,在Derived班级,我们有另一名成员。

class Base
{
private:
    double d;
protected:
    long l;
public:
    int i;
};

class Derived : public Base
{
private:
    float f;
};

4 个答案:

答案 0 :(得分:51)

恰好你的类Base有8字节对齐要求,但它的最后一个成员大小为4.这导致在Base内存布局末尾添加了一个空填充区域。当你自己实例化类Base的对象时,额外的填充扮演它的角色,就像所谓的派生最多的对象

Base b; // <- a most-derived object
Base a[10]; // <- an array of most-derived objects

但是,当您将Base作为基类“嵌入”类Derived时,嵌入式Base子对象的末尾不需要额外的填充。

Derived d; // <- object `d` contains an embedded sub-object of type `Base`

智能编译器将尝试通过将类Derived的额外字段放入用于Base中的填充的布局区域来重用该区域。在你的情况下,额外的字段Derived::f偶然具有相同的4字节大小,即它完全适合那里。最终结果是该类的总大小不会增加。

非常相似(实际上)的效果是所谓的“空基优化”。在C ++中,任何类型的sizeof都保证大于0,这意味着空类的sizeof总是大于零。但是,当您从空基类派生其他类时,您可能会发现基类对派生类的大小贡献了0个字节。例如

struct A {};
struct B {};
struct C {};
struct D {};

struct F : A, B, C, D {
  int i;
}

int main() {
  std::cout << sizeof(A) << std::endl << sizeof(B) << std::endl << 
               sizeof(C) << std::endl << sizeof(D) << std::endl;
  std::cout << sizeof(F) << std::endl;
}

即使每个基类的sizeof大于零,sizeof(F)通常仍会计算为sizeof(int),就像基类子对象根本不存在一样。

换句话说,正如这些示例所示,基类子对象在其内存布局方面遵循明显比大多数派生对象更宽松的规则。这些宽松的规则可能很容易导致基类的sizeof仅部分贡献于派生类的sizeof的情况。

答案 1 :(得分:9)

因为你有sizeof(double)== sizeof(long)== 8而且它通常意味着alignof(double)也等于8。这意味着Base必须在8字节边界上进行大小对齐,以防它存储在数组中,并且它在结尾处生成4字节填充,Derived会删除它以放置f。

答案 2 :(得分:5)

使用pahole来弄明白:

class Base {
private:

    double                     d;                    /*     0     8 */
protected:

    long int                   l;                    /*     8     8 */
    int                        i;                    /*    16     4 */


    /* size: 24, cachelines: 1, members: 3 */
    /* padding: 4 */
    /* last cacheline: 24 bytes */
};
class Derived : public Base {
public:

    /* class Base                <ancestor>; */      /*     0    24 */

    /* XXX last struct has 4 bytes of padding */
private:

    /* Bitfield combined with next fields */

    float                      f;                    /*    20     4 */


    /* size: 24, cachelines: 1, members: 2 */
    /* paddings: 1, sum paddings: 4 */
    /* last cacheline: 24 bytes */
};

答案 3 :(得分:2)

由于对齐需要填充:

  

虽然编译器(或解释器)通常会分配个人   对齐边界上的数据项,数据结构通常有成员   具有不同的对齐要求。保持正确对齐   翻译通常会插入其他未命名的数据成员   每个成员都正确对齐。另外还有数据结构   作为一个整体可以填写最后一个未命名的成员。这允许每一个   要正确对齐的结构数组的成员。

此处有更多相关内容:
http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding