5

假设我有任何类型的类或结构。没有虚函数或任何东西,只有一些自定义构造函数,以及一些需要在析构函数中清理的指针。

在这个结构上使用 memcpy 或 memmove 会有任何不利影响吗?删除移动的结构会导致问题吗?该问题假设内存对齐也是正确的,并且我们正在复制到安全内存。

4

5 回答 5

9

在一般情况下,是的,会有问题。memcpy和都是memmove按位运算,没有进一步的语义。这可能不足以移动对象*,显然也不足以复制

在复制的情况下,它将中断,因为多个对象将引用同一动态分配的内存,并且多个析构函数将尝试释放它。请注意,类似的解决方案shared_ptr在这里没有帮助,因为共享所有权是 / 不提供的进一步语义的一部分。memcpymemmove

对于移动,并且根据类型,您可能会在某些情况下侥幸逃脱。但是,如果对象持有指向被移动元素的指针/引用(包括自引用),则它将不起作用,因为指针将按位复制(同样,没有复制/移动的进一步语义)并将引用旧位置。

一般的答案还是一样的:不要


*不要在 C++11 的意义上采取行动。我已经看到了标准库容器的实现,它使用特殊标记来启用移动对象,同时通过使用 来增加缓冲区memcpy,但它需要在存​​储类型中显式注释,将对象标记为可安全移动memcpy,在对象被放入之后。新缓冲区旧缓冲区被丢弃而不调用任何析构函数(C++ 11移动需要使对象处于可破坏状态,这无法通过此 hack 实现)

于 2012-08-27T03:16:54.860 回答
5

通常在基于类的对象上使用 memcpy 不是一个好主意。最可能的问题是复制指针然后删除它。您应该改用复制构造函数或赋值运算符。

于 2012-08-27T03:04:26.460 回答
4

不,不要这样做。

如果您memcpy的结构的析构函数删除了自身内部的指针,那么当结构的第二个实例以任何方式被破坏时,您最终会执行双重删除。

C++ 习惯用法是类的复制构造函数,std::copy或者它的任何朋友用于复制范围/序列/容器。

于 2012-08-27T03:05:43.233 回答
4

如果您使用的是 C++11,则可以使用std::is_trivially_copyable来确定是否可以使用memcpyor复制或移动对象memmove。从文档中:

普通可复制类型的对象是唯一可以使用 std::memcpy 安全复制或使用 std::ofstream::write()/std::ifstream::read() 序列化到二进制文件/从二进制文件序列化的 C++ 对象。通常,可简单复制的类型是可以将其底层字节复制到 char 或 unsigned char 数组以及相同类型的新对象的任何类型,并且生成的对象将具有与原始对象相同的值。

许多类不符合此描述,您必须注意类可能会更改。我建议如果您要在 C++ 对象上使用memcpy/ ,您应该以某种方式保护不需要的使用。memmove例如,如果你正在实现一个容器类,那么容器所持有的类型很容易被修改,这样它就不再是可复制的(例如,有人添加了一个虚函数)。你可以这样做static_assert

template<typename T>
class MemcopyableArray
{
    static_assert(std::is_trivially_copyable<T>::value, "MemcopyableArray used with object type that is not trivially copyable.");
    // ...
};
于 2016-10-19T10:00:44.637 回答
3

除了安全性(正如其他答案已经指出的那样是最重要的问题)之外,还可能存在性能问题,尤其是对于小型物体。

即使对于简单的 POD 类型,您也可能会发现在复制构造函数的初始化程序列表中进行适当的初始化(或根据您的用法在赋值运算符中进行赋值)实际上甚至比memcpy. 这很可能是由于 memcpy 的入口代码可能会检查字对齐、重叠、缓冲区/内存访问权限等。在 Visual C++ 10.0 及更高版本中,举个具体的例子,你会惊讶于在 memcpy 甚至开始其逻辑功能之前,就会执行许多测试各种事物的序言代码。

于 2012-08-27T03:14:51.317 回答