【问题标题】:C++ POD struct inheritance? Are there any guarantees about the memory layout of derived membersC++ POD结构继承?派生成员的内存布局是否有任何保证
【发布时间】:2014-04-19 17:02:45
【问题描述】:

假设我有一个struct RGB,我想创建struct RGBA,它继承RGB

struct RGB {
    unsigned char r;
    unsigned char g;
    unsigned char b;
};

struct RGBA: RGB {
    unsigned char a;
};

两者都将用于读取未压缩的图像数据:

RGBA *pixel=static_cast<RGBA *>(image->uncompressed_data);

问题:关于struct RGBA 的内存布局,这样安全吗?有没有人保证:

  • unsigned char a 出现在 RGB struct 之后(不是之前)
  • struct RGB 和来自struct RGBA 的a 参数之间没有填充?

#pragma pack 会帮忙吗?这都是关于继承期间的内存布局。

【问题讨论】:

  • 标准中不保证rg 之间没有填充。标准中也没有保证RGBA 所需的对齐方式。因此,即使不必回答您提出的实际问题,这也不安全。
  • 只需创建一个带有布尔值的 RGBA 类,它指示是否使用 Alpha 通道。
  • 你还需要考虑字节序。只需编写执行所需按位操作的打包/解包函数,您就可以省去很多麻烦。

标签: c++ inheritance


【解决方案1】:

我可能错了,但在我看来你可以定义:

struct RGBA {
    struct RGB base;
    unsigned char g;
};

并且 (1) RGBA 将是 POD,并且 (2) 您可以双向投射:

RRBA x;
RGB* x_rgb = reinterpret_cast<RGB*>(&x);
RGBA* x_rgba = reinterpret_cast<RGBA*>(x_rgb);

我怀疑会有一些填充可能会被 attribute((packed)) 消除。

【讨论】:

【解决方案2】:

正如这里所有先前的答案已经说明的那样:标准不保证。但是,如果您仍然需要这样的 POD 继承(派生类实际上不再是 POD),您至少可以通过使用 static_assert 验证当前编译器的行为是否符合要求。如果您切换到另一个行为不同的编译器,那么您至少应该得到编译器错误。但是,如果您的代码应该易于移植,这可能是一种不好的做法。

我还建议使用编译器变量属性__attribute__((packed)) 来声明您的POD 结构并使用&lt;cstdint&gt;。这适用于例如GCC,但也可能因您使用的编译器而异。

#include <cstdint>

struct __attribute__((packed)) RGB {
    uint8_t r;
    uint8_t g;
    uint8_t b;
};

struct __attribute__((packed)) RGBA : RGB {
    uint8_t a;
};

static_assert( sizeof(RGB) == 3u, "Unexpected size" );
static_assert( sizeof(RGBA) == 4u, "Unexpected size" );

【讨论】:

    【解决方案3】:

    不,不保证布局。唯一的保证是标准布局类;而这样一个类的条件之一是它

    要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类

    换句话说,所有数据成员必须在同一个类中,不能超过一个。

    【讨论】:

      【解决方案4】:

      检查填充很容易:打印sizeof(RGB)sizeof(RGBA)。如果不是 3 个相应的 4 个,则结构被填充,您需要将其删除。

      如果成员ab之后,可以使用offsetof查看每个成员的偏移量。如果a 的偏移量比b 的偏移量大一,那么a 就在b 之后。

      【讨论】:

      • 使用编译器 XYZ 检查它只是告诉编译器是如何实现它的——而不是它是否符合标准的行为。
      • @BЈовић 由于 OP 想知道是否需要 #pragma pack,我猜 OP 有一个特定的编译器,不用担心可移植性。
      • 我不知道#pragma pack 是做什么的,也不知道它适用于哪个编译器。但我认为他们想知道标准的内容。
      • @BЈовић #pragma pack 强制结构具有一定的对齐方式,覆盖编译器通常分配的任何对齐方式。它得到 MSVC、GCC 和 Clang 的支持。
      【解决方案5】:

      对于派生成员的内存布局有NO保证,并且演员表是安全的。

      因为你有继承,也可能有填充,这是微不足道的。

      § 9 类

      1 POD struct109 是一个既是普通类又是标准布局类的类,并且没有非 POD 结构、非 POD 联合类型的非静态数据成员 (或此类类型的数组)。类似地,POD 联合是一个既是普通类又是标准布局类的联合,并且没有非-

      另外,std::is_pod&lt;RGBA&gt; 不是 POD

      std::cout << std::boolalpha;
      std::cout << std::is_pod<RGBA>::value << '\n';
      

      结果为假。见live demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-17
        • 2014-11-27
        • 2012-07-09
        • 1970-01-01
        • 2017-04-14
        • 1970-01-01
        • 2015-09-01
        • 2019-09-03
        相关资源
        最近更新 更多