让我们说,我有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
的参数之间没有填充? #pragma pack
会在这里提供帮助吗?它是继承期间内存布局的全部内容。
答案 0 :(得分:15)
不,布局无法保证。唯一的保证是标准布局类;这类课程的一个条件是它
在大多数派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有包含非静态数据成员的基类
换句话说,所有数据成员必须属于同一个类,而不是多个。
答案 1 :(得分:9)
对于派生成员的内存布局有 NO 保证,并且演员不安全。
由于你有继承,也可能有填充,这是不琐碎。
§9课程
1 POD struct109是既是普通类又是标准布局类的类,并且没有非POD结构,非POD联合类型的非静态数据成员(或这些类型的数组)。类似地,POD联合是一个既简单的类又是标准的布局类的联合,并且没有非
std::is_pod<RGBA>
也不是POD
std::cout << std::boolalpha;
std::cout << std::is_pod<RGBA>::value << '\n';
结果是假的。见live demo
答案 2 :(得分:2)
检查填充很容易:打印sizeof(RGB)
和sizeof(RGBA)
。如果它不是3个相应的4那么结构被填充,你需要将其删除。
至于成员a
是否在b
之后,您可以使用offsetof
来检查每个成员的偏移量。如果a
的偏移量大于b
的偏移量,则a
后面的b
会直接出现。{/ p>
答案 3 :(得分:0)
正如这里所有先前的答案已经指出的那样:标准不保证它。但是,如果您仍然需要 POD 的这种继承(派生类实际上不再是 POD),您至少可以通过使用 static_assert 验证当前编译器的行为是否符合要求。如果你切换到另一个行为不同的编译器,那么你至少应该得到编译器错误。不过,如果您的代码应该易于移植,这可能是不好的做法。
我还建议使用编译器变量属性 __attribute__((packed))
来声明您的 POD 结构并使用 <cstdint>
。这对例如有效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" );