为什么带有析构函数的类不能被memcpy'ed

时间:2016-09-21 10:56:11

标签: c++

C ++规则说它是合法的,可以使用memcpy复制对象或 POD 类型。

他们进一步说POD不能有(非平凡的)析构函数。为什么这就是为什么仅仅添加一个析构函数会改变这个类,以至于使用memcpy不起作用?

// Perfectly fine to copy using memcpy
struct data
{
    int something;
    float thing;
};

// Not allowed to copy using memcpy
int var;

struct data
{
    int something;
    float thing;
    ~data() { var = 1; }
};

为什么简单地添加析构函数会导致无法记忆结构的数据?我无法想象这需要以任何方式改变数据布局。

我对被告知不要这样做不感兴趣,我无意这样做......我明白我不能这样做因为“标准这么说”但我想知道是什么原因标准说是这样,因为它似乎不是对我的必要限制,并想了解原因。

编辑人们似乎误解了我的问题。我不是在问使用memcpy是不是一个好主意。我问的是,如果有一个非平凡的析构函数,那么使其成为非法的原因是什么。我看不出它有什么不同,并且想要了解为什么存在这种限制。我得到的关于它是一个坏主意的大多数原因同样适用于如果有一个析构函数。

6 个答案:

答案 0 :(得分:4)

通俗地说:

  

为什么简单地添加析构函数会导致无法记忆结构的数据?

它不会使它变得不可能,只是非法。

  

我无法想象这需要以任何方式改变数据布局。

可能赢了,但它被允许了。因为该类不再是POD(即c结构),所以它现在是一个c ++类。

类对POD有不同的规则。由于我们无法预测编译器将如何对其进行编码,因此我们无法再推断memcpy的结果。

答案 1 :(得分:1)

非平凡的析构函数通常会反转在影响对象状态的构造函数(或其他成员函数)中执行的一些非平凡的操作。

memcpy()复制构成对象的位。如果构造函数的行为会给出一组不同的位,那么该对象上的析构函数将尝试反转某些实际上没有发生的操作。

一个典型的例子是一个类的构造函数分配一些资源(例如内存),其他成员函数确保资源保持在一个合理的状态,析构函数释放该资源。使用memcpy()复制此类对象将复制该资源的句柄,而不是为复制的对象创建该资源的新实例[这是此类对象的复制构造函数通常所做的]。析构函数 - 最终 - 将为原始对象和复制对象调用,资源将被释放两次。对于内存(例如,使用C' s malloc()或C ++的运算符new分配),释放两次会给出未定义的行为。对于其他资源(文件句柄,互斥体,其他系统资源),结果会有所不同,但是 - 必须在系统上 - 通常不建议将单个内容解除分配两次。

另一个问题是一个类可能有基类,或者有成员,它们本身就有非平凡的构造函数和析构函数。即使类本身有一个什么也不做的构造函数,销毁一个对象会调用所有成员和基础的析构函数。使用memcpy()复制此类对象会以我上面描述的方式影响这些基类或成员。

答案 2 :(得分:0)

只能使用memcpy复制易于复制的对象。具有非平凡析构函数的类不是简单的可复制的。

假设一个例子,你的类有一个指针作为其成员之一。您在类的构造函数中为该指针分配空间。现在,您可以在非平凡的析构函数中执行各种操作,例如删除分配的空间。通过memcpy,您将逐位复制整个结构。因此,当调用析构函数时,两个实例将尝试删除相同的指针。

答案 3 :(得分:0)

问题通常是当你有一个析构函数做某事时,你也应该有一个复制构造函数/赋值运算符(为此查找“Rule of 3”)。

当你记忆时,你将跳过这些复制操作,这会产生一些后果。

E.g。你有一个指向对象的指针,并在构造函数中删除它。然后,您还应指定复制操作,以便在那里复制指针/对象。如果你使用memcpy而不是你有两个指向同一个实例的指针而第二个破坏会导致错误。

编译器无法知道你在析构函数中做了什么,以及是否需要特殊行为,因此它是悲观的,并且被视为非POD类型。 (即使你在析构函数中什么都不做)。

当你在c ++ 11中的类中声明析构函数时,生成move-assignment / move-constructors会发生类似的事情。

答案 4 :(得分:0)

这是因为memcpy提供了一个浅拷贝,如果你有一个非平凡的dtor,可能是因为你的对象是某个资源的所有者,那么浅拷贝不能为你提供正确的拷贝语义(重复)所有权)。考虑一些带有内部指针的结构,当结构消失时,dtor应该(可能)释放资源,但是浅拷贝会让你有一个悬空指针。

答案 5 :(得分:-1)

当内存归类所有时出现问题:你不应该记忆拥有内存的类(即使它没有析构函数),例如:

https://ideone.com/46gFzw

#include <iostream>
#include <memory>
#include <cstring>

struct A
{
  std::unique_ptr<int> up_myInt;
  A(int val)
  :
    up_myInt(std::make_unique<int>(val))
  {}
};

int main()
{
    A a(1);
    {
      A b(2);
      memcpy(&a, &b, sizeof(A));
      std::cout << *a.up_myInt << std::endl;
      //b gets deleted, and the memory b.up_myInt points to is gone
    }
    std::cout << *a.up_myInt << std::endl;
    return 0;
}

导致

标准输出

2
0

stderr的

*** Error in `./prog': double free or corruption (fasttop): 0x08433a20 ***

b超出范围时,它拥有的数据被删除,a指向相同的数据,因此有趣的时间(如果您的类基本上包含任何其他stl容器,则会发生同样的事情,所以永远不会memcpy也是一个stl包含。)