【问题标题】:What is the difference between memberwise copy, bitwise copy, shallow copy and deep copy?成员复制、按位复制、浅复制和深复制有什么区别?
【发布时间】:2017-03-12 15:44:26
【问题描述】:

我已经用谷歌搜索了这些术语,但我仍然感到困惑。

有人说成员复制是深复制,按位复制是浅复制,但有人说不是。

谁能给我解释一下默认拷贝构造函数和用户自定义拷贝构造函数使用哪种类型的拷贝?

【问题讨论】:

  • 一个深拷贝可能包含拷贝构造,而按位拷贝不包含。
  • 如果这个问题实际上“太宽泛”,那么您会期望看到大量的答案,但没有一个能够给出令人满意的覆盖范围。正如我们所看到的,到目前为止,只有两个答案,而且他们都做得相当不错。请不要仅仅因为您厌倦了编写足够长的文本来覆盖它,就投票结束一个问题。还有其他人喜欢写作,并且会写作。

标签: c++ oop


【解决方案1】:

会员复制

当您访问每个成员并显式复制它时,调用其复制构造函数。它通常等同于深拷贝。这是复制事物的正确方法。相反的是按位复制,这是一种 hack,见下文。

按位复制

是一种特定形式的浅拷贝。当您使用memcpy() 或类似的东西简单地将源类的位复制到目标类时。构造函数没有被调用,所以你倾向于得到一个看起来没问题的类,但是一旦你开始使用它,事情就会以可怕的方式出现。这与成员复制相反,是一种快速而肮脏的技巧,当我们知道没有要调用的构造函数和要复制的内部结构时,有时可以使用它。有关可能出现问题的讨论,请参阅此问答:C++ bitwise vs memberwise copying?

浅拷贝

指只复制对象的直接成员,而不复制它们指向的任何结构。这是您进行逐位复制时得到的结果。

(请注意,没有“影子副本”之类的东西。我的意思是,在文件系统中存在这样的东西,但这可能不是您的想法。 )

深拷贝

不仅指复制对象的直接成员,还指复制它们指向的任何结构。这是您在进行按成员复制时通常会得到的结果。

总结一下:

有两类:

  • 浅拷贝
  • 深拷贝

然后,有两种广泛使用的技术:

  • 按位复制(浅复制的一种形式)
  • Member-wise Copy(深度复制的一种形式)

至于有人说某事有人说某事的传闻:按位复制肯定总是浅复制。成员级复制通常是深度复制,但您当然可能会弄乱它,因此您可能认为您正在制作深度复制,而实际上并非如此。正确的成员复制依赖于正确的复制构造函数。

最后:

如果已知对象可简单复制,则默认复制构造函数将执行按位复制,否则将执行按成员复制。但是,编译器并不总是有足够的信息来执行每个成员的正确副本。例如,通过复制指针而不是复制指向的对象来复制指针。这就是为什么当您的对象不可轻易复制时,您通常不应依赖编译器为您提供默认的复制构造函数。

用户提供的构造函数可以执行用户喜欢的任何类型的复制。希望用户能够明智地选择并按成员进行复制。

【讨论】:

  • C++11 模板std::is_trivially_copyable 旨在确定浅拷贝何时有效,特别是memcpy() 何时有效。
  • “例如,通过复制指针而不是复制指向的对象来复制指针。”我一直在寻找名称背后的原因,这句话以某种方式为我连接了点,谢谢!
【解决方案2】:

简短版:

  • 按位复制是内存块的复制
  • 会员副本是需要深入了解以下内容的副本 被复制的数据结构

说明差异的最简单方法是使用指针成员:

  • 通过按位复制,您只需要复制 它指向的内存。
  • 通过执行成员复制,您将复制内存 指针指向,然后初始化复制的成员指针 带有新内存区域的地址

例子:

struct A 
{
   int* m_a;

   A() 
   {
      m_a = new int[1];
   }

   ~A()
   {
     delete [] m_a;
   }
};

A obj;

printf("%p\n", obj.m_a);  // prints: 0x10001000 (for example)

按位复制

A copiedObj = obj;        // the default copy constructor is employed
                          // it will perform a bitwise copy

printf("%p\n", copiedObj.m_a);  // prints: 0x10001000 - the same address

现在 obj.m_a 和copyedObj.m_a 都指向内存中的同一个地址。

注意,对于这样的类定义,如果您尝试删除这两个对象,您将遇到内存访问异常(作为第二个要删除的对象将尝试释放已被释放的内存) .

会员复制

struct A 
{
    A(const A& rhs)  // let's define a custom copy constructor
    {
        m_a = new int[1];
        memcpy(m_a, rhs.m_a, sizeof(int) * 1);
    }
}

A copiedObj = a;

printf("%p\n", copiedObj.m_a);  // prints: 0x9a001234 - a totally different address

我们在自定义复制构造函数中执行的自定义操作复制了原始对象指针指向的内存。

最后但并非最不重要的一点 - 成员复制不限于复制构造函数。可以将其视为对对象所在的内存区域进行操作的操作。

【讨论】:

  • 两者是一回事。我的意思是通过执行按位复制,只会复制指针的内容。指针只是一个包含内存地址的 4(或 8)字节长值。只会复制该值,而不是地址指向的内存。
【解决方案3】:

四个术语:

  • 会员复制
  • 按位复制
  • 浅拷贝
  • 深拷贝

不存在于同一意义轴上。

深拷贝是大多数人在大多数情况下对编程中语义对象的期望,当他们说:“计算机,给我一份”。

具体来说,在 C++ 的上下文中,这意味着现有对象的副本也递归地复制了所有子对象,无论它们是如何实现的。 (指针,ints,其他复杂对象,...)

浅拷贝是当你没有考虑到对象的一些直接内存成员实际上是指针(某种类型)并且你只复制它们的“指针”时得到的结果value”,而不是遵循间接并创建指向对象的正确副本。

当编译器执行简单的成员复制(见下文)并且您的对象包含(原始)指针时,您在 C++ 中得到的就是浅复制。如果您对任何对象进行简单复制,浅拷贝也是您在 Java 或 C# 中得到的,因为在那里,“一切”都是指针/引用。

如果您有一个可简单复制的对象,例如struct 只包含 ints 那么浅拷贝与深拷贝相同。

按位复制是您在 C++ 中对内存中对象执行 memcpy() 时得到的结果。结果可能是语义上的深拷贝,或者(更可能)是浅拷贝(如果涉及指针),或者它可能只导致内存中的一堆位不再是被复制类型的有效对象的形式来自。

最后,Memberwise Copy 是 C++ 中的一个特定术语,由 C++ Std 调用,用于说明编译器生成的 C'tor 副本 em> 将在被调用时执行。具体来说:

非联合类 X 的隐式定义的复制/移动构造函数执行其基类和成员的成员复制/移动。 (...)

[class.copy.ctor] 中,它继续描述这意味着什么:

每个基本或非静态数据成员都以适合其类型的方式复制/移动:(...)

(!!) 成员复制可以在语义上等效于上述 3 种类型中的所有,具体取决于对象。

(此时我注意到 C++ 标准始终使用“memberwise”,而不是“member-wise”,但我的自动更正更喜欢“member-wise”或“member-wise”——这在谷歌搜索时可能会有所帮助。)

就我而言,如果您有用户定义的复制 ctor,您永远不会(应该)看到 1:1 成员复制,因为如果您做 1:1 成员复制,你不需要实现 ctor,编译器会为你生成它。

做几个比较:

  • 如果你有一个structof ints,那么按成员复制的结果与按位复制相同,它是浅复制和深复制(嗯,没有深度 t 说话)。
  • 如果你有一个structint* 指针,比成员复制和按位复制一样,也是浅复制,但不再是深复制,因为复制会指向与原始整数相同。
  • 如果您有一个包含任何重要对象的结构(例如std::string),则按位复制不再有效,并且成员复制是浅还是深取决于子对象的实现:如果子对象都在复制构造上实现深复制,则副本是深的。如果您有任何只执行浅拷贝的子对象成员,那么逐个成员的拷贝也将是(部分)浅拷贝。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 2023-04-05
    • 2015-11-23
    • 2013-02-13
    • 2011-04-26
    • 2016-09-24
    相关资源
    最近更新 更多