3.9 / 2:
对于任何对象(除了基类子对象)之外的任何对象 可复制类型T,无论对象是否包含有效的类型值 T,构成对象的底层字节(1.7)可以复制到 char或unsigned char的数组。
3.9 / 3:
对于任何简单的可复制类型T,如果指向T的两个指针指向 不同的T对象obj1和obj2,其中既不 obj1 也不 obj2是 基类子对象,如果构成obj1的基础字节(1.7)是 复制到obj2中,obj2随后应保持与obj1相同的值。
我正式理解这些规则,但我感兴趣的是这些限制的重点是什么?
答案 0 :(得分:9)
基类子对象可能在派生类使用的末尾有填充。鉴于两个类,
struct A {
int a;
char b;
};
struct B : A {
char c;
};
完全有可能是sizeof(A) == sizeof(B)
。如果他们是平等的,那么如果你只是使用memcpy
来复制A
子对象,那么事情应该很明显:你将无法阻止阅读甚至覆盖{{1} }值。
您的实现可能会也可能不会重复使用这样的填充。设计填充不重复使用的ABI的正当理由正是为了很好地处理那些错误地将c
用于此类子对象的代码。
注释给出了一个空基类的例子。这是当前实现很可能重用基类字节的一种特殊情况,但它不是唯一允许的时间。
答案 1 :(得分:0)
这是一个由基类子对象和EBO引起的价值搞乱的例子:
#include <cassert>
#include <iostream>
struct Base {}; // empty class
struct Derived1 : Base {
public:
int i;
};
int main()
{
// the size of any object of empty class type is at least 1
assert(sizeof(Base) == 1);
// empty base optimization applies
assert(sizeof(Derived1) == sizeof(int));
Base objBase;
Derived1 objDerived;
objDerived.i = 42;
Base& refToobjDerived = objDerived;
char buf[sizeof(Base)]; // 1
std::memcpy(buf, &objBase, sizeof(Base)); // copy objBase to buf
// might do something with buf..
std::memcpy(&refToobjDerived, buf, sizeof(Base)); // EBO! I'm overwriting the int's value!
std::cout << objDerived.i; // Screwed
}
如果渲染基类non-trivially-copyable,则不会触及该值。
hvd突出显示的另一个问题可能在于基类末尾的附加填充,用于存储派生拥有的数据。