memcpy 一个非 POD 对象

Posted

技术标签:

【中文标题】memcpy 一个非 POD 对象【英文标题】:memcpy a non-POD object 【发布时间】:2014-10-07 03:05:41 【问题描述】:

对于 POD 类型的对象,标准保证当您将对象的内容 memcpy 到一个 char 或 unsigned char,然后将内容 memcpy 回您的对象,该对象将保持其原始值。

现在请注意,对于非 POD 类型的对象没有这样的保证。所以我的问题是为什么会这样?

Source of the text above

【问题讨论】:

memcpy 不能跟随引用/指针,或在复杂对象上调用复制构造函数。它所做的只是获取一块连续的内存,并将其复制到其他地方。 @Kam:考虑通过memcpy 复制std::string。现在有两个对象引用同一个缓冲区。他们每个人都会尝试释放缓冲区。 @Kam:“浅”副本通常对非 POD 类型无效。例如,如果您复制了 std::vector 的字节(包含指向它拥有的内存的指针),然后修改了原始字节(导致它重新分配该内存),然后将字节复制回来,它现在将是无效的,指到它在重新分配之前拥有的内存。因此,您将不再拥有原始的“价值”,而是一些破碎的东西。 @imreal:所有对象,无论是否微不足道,都占用连续字节。我举了一个例子,说明复制非平凡类型的字节如何产生无效行为。 @imreal:不,谢谢,这对我来说太形而上学了。我会让其他人争论“存储区域”是否可以表示连续的字节范围之外的任何内容。 【参考方案1】:

普通可复制类的原因(C++11 主要使用概念普通类标准布局类而不是POD) 可以被 memcpy'ed 与其他答案/cmets 建议的动态分配无关。当然,如果您确实尝试了具有动态分配的类型的浅拷贝,那么您就是在招惹麻烦。但是您很可能拥有一个带有指针的类型,该指针在用户提供的构造函数中进行动态分配(只要它具有默认构造函数)并符合普通类

可以保证 memcpy 的实际原因是 普通可复制(以及 标准布局)类型需要占用连续字节的存储空间,而其他对象则不需要.

N3690

1.8.5 除非它是位字段 (9.6),否则最衍生的对象应具有非零大小并应占用一个或多个 字节的存储空间。基类子对象的大小可能为零。可简单复制或标准布局的对象 类型 (3.9) 应占用连续字节的存储空间。

【讨论】:

@BenVoigt 你是对的,但是普通类只是增加了 2 个要求,所以它更通用。 不。 trivially copyable 更通用(更多类型符合条件)。它还可以使您免于“(只要它具有默认构造函数)”的错误 @BenVoigt 哎呀,你是对的,更多的要求意味着更多的限制。我会改的。 BTW 普通类需要一个普通的默认构造函数。仅仅有一个零参数,甚至是默认的默认构造函数是不够的。 非连续存储的具体示例:想象一个 16 位分段架构,其中 vtable 指针是近指针 ... memcpy'ing 对象到不同的段会破坏 vtable 指针.【参考方案2】:

我不确定严格来说该标准是否允许您将 memcpy 放入 char 的数组中并再次返回,尽管您肯定会使用 POD 侥幸逃脱。

但是当您开始研究 C++ 可以做的所有复杂事情时,事情就会变得更加模糊。考虑以下几点:

struct ex1;

struct ex2

    ex2(std::unique_ptr<ex1> ptr) : memberptr 
private:
    std::unique_ptr<ex1> member;
;

结构ex2 是只移动的,因为它有一个只移动的成员变量。那么如果我们使用memcpy 来构造ex2 实例的逐位相同副本会发生什么?我们最终得到了两个都认为自己拥有成员指针的对象。当其中第二个被删除时会发生什么?你明白了。

【讨论】:

字节数组不拥有任何东西。问题是为什么您不能将非 POD 存储到字节数组然后将其复制回来,而不是为什么您不能将其存储到另一个非 POD 然后使用另一个非 POD。除了该语言没有所有权概念之外,它是一个更高级别的构造,您可以将其应用于 bith 智能指针和常规指针(它们是 POD)。【参考方案3】:

这种序列化失败的特殊情况是具有虚拟成员的类型。如果一个类型有一个虚拟成员,那么它就有一个 vtable。该表将包含指向每个虚拟成员的实现的指针。

如果 char 数组中的序列化数据跨越进程边界(您通过网络发送它,或者将其写入磁盘并从不同进程读回),那么您写出的 vtable 指针可能不再是有效,并且调用任何虚拟成员都会导致未定义的行为。

【讨论】:

如果你跨越了进程边界,那么无论你在哪里复制绝对不是“回到你的对象”。这是一个完全不同的对象。这不是这个问题的主题。

以上是关于memcpy 一个非 POD 对象的主要内容,如果未能解决你的问题,请参考以下文章

在 c++/c++11 中测试“POD-ness”?

用 memcpy “构造”一个​​可简单复制的对象

相邻内存区域上的 memcpy() 安全性

memcpy 是可简单复制的类型构造还是赋值?

k8s之Service资源

按值、常量值、引用或常量引用传递非 POD 类型