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

Axa*_*ano 40 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;
};
Run Code Online (Sandbox Code Playgroud)

AnT*_*AnT 51

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

Base b; // <- a most-derived object
Base a[10]; // <- an array of most-derived objects
Run Code Online (Sandbox Code Playgroud)

但是,当您将"嵌入" Base作为基类进入类Derived时,在嵌入的Base子对象的末尾不需要额外的填充.

Derived d; // <- object `d` contains an embedded sub-object of type `Base`
Run Code Online (Sandbox Code Playgroud)

智能编译器将尝试通过将类的额外字段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;
}
Run Code Online (Sandbox Code Playgroud)

即使sizeof每个基类大于零,sizeof(F)通常仍会评估sizeof(int),就好像基类子对象根本不存在一样.

换句话说,正如这样的例子所示,基类子对象关于它们的存储器布局遵循比大多数派生对象明显更宽松的规则.这些宽松的规则可能很容易导致sizeof基类只sizeof对派生类产生部分贡献的情况.

  • @Marc Glisse:ABI是一个与不同领域不同的故事.谁说甚至还有ABI?就语言本身而言,没有ABI这样的东西.一个更相关的说法可能是用户实际上可以将这些事情由编译器决定,他们可以操纵不同的依赖于实现的编译器工具,这些工具允许覆盖默认的对齐/打包规则. (3认同)
  • 如何计算对齐要求,即基于什么?它是在数据总线大小? (2认同)

gal*_*p1n 9

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

  • 填充与`alignof(double)`比与'sizeof(double)`更相关. (7认同)

pep*_*ppe 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 */
};
Run Code Online (Sandbox Code Playgroud)