成员复制,按位复制,浅拷贝和深拷贝有什么区别?

时间:2017-03-12 15:44:26

标签: c++ oop

我搜索了这些条款,但我仍然感到困惑。

有些人说成员副本是深拷贝,而按位副本是影子副本,但有人说它不是。

有人可以向我解释默认复制构造函数和用户定义的复制构造函数的哪种类型的副本使用?

3 个答案:

答案 0 :(得分:10)

会员身份复制

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

逐位复制

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

浅拷贝

指仅复制对象的直接成员,而不复制它们指向的任何结构。这是你在做一个有点复制的时候得到的。

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

深层复制

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

总结:

有两类:

  • 浅拷贝
  • 深层复制

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

  • 按位复制(一种浅拷贝形式)
  • 会员级复制(深层复制形式)

至于听说 - 有人说某事和某人说了别的话:有点复制肯定是浅拷贝。会员明智的副本通常是深层复制,但你当然可以搞砸,所以你可能会认为你正在制作一份很深的副本,而事实上你并非如此。正确的成员复制依赖于拥有适当的复制构造函数。

最后:

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

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

答案 1 :(得分:0)

简短版本:

  • 按位复制是内存块的副本
  • 成员副本是需要密切了解的副本 正在复制的数据结构

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

  • 通过执行按位复制,您只需要复制地址 记忆它指向。
  • 通过执行成员复制,您可以复制内存 指针指向,然后初始化复制的成员指针 使用新内存区域的地址

示例:

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和copiedObj.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

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

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

答案 2 :(得分:0)

四个术语:

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

不存在于同一意义轴上。

深度复制是大多数人在大多数情况下对编程中的语义对象的期望:“计算机,给我一个副本”。

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

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

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

如果您有一个可简单复制的对象,例如一个只包含 structint 那么浅拷贝和深拷贝是一样的。

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

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

<块引用>

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

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

<块引用>

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

(!!) Memberwise Copy 可以语义等价于上述 3 种类型中的所有,具体取决于对象。

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

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

做一些比较:

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