我已经谷歌了这些术语,但仍然感到困惑。
有些人说成员逐一复制是深拷贝,位拷贝是浅拷贝,但也有人说不是这样的。
有人能解释一下默认拷贝构造函数和用户自定义拷贝构造函数使用哪种类型的拷贝吗?
我已经谷歌了这些术语,但仍然感到困惑。
有些人说成员逐一复制是深拷贝,位拷贝是浅拷贝,但也有人说不是这样的。
有人能解释一下默认拷贝构造函数和用户自定义拷贝构造函数使用哪种类型的拷贝吗?
逐成员复制
是指访问每个成员并显式地复制它,调用其复制构造函数。这是复制事物的正确方法。如果做得对,它就相当于深层复制,因为你调用的每个具有复制构造函数的成员将(或应该)反过来执行其自己成员的逐成员复制,如此类推。相反的是按位复制,这是一个hack,见下文。
按位复制
是一种特定形式的浅拷贝。它是指仅仅复制源类的位到目标类,使用memcpy()
或类似的东西。构造函数不会被调用,所以你往往会得到一个看起来很好的类,但是一旦你开始使用它,事情就会以可怕的方式开始破裂。这是逐成员复制的相反,是一种快速而肮脏的hack,有时可以在我们知道没有要调用的构造函数和没有要复制的内部结构时使用。关于这可能出现的问题的讨论,请参见此Q&A:C++按位与逐成员复制?
浅拷贝
是指仅复制对象的直接成员,而不复制这些成员所指向的任何结构。这就是当你进行按位复制时得到的结果。
(注意:没有“阴影拷贝”这种东西。我的意思是,在文件系统中确实有这样的东西,但那可能不是你想要的。)
深层复制
是指不仅复制对象的直接成员,而且还复制这些成员所指向的任何结构。这通常是在进行逐成员复制时得到的结果。
总之:
有两个类别:
然后,有两种广泛使用的技术:
至于那些关于某人说了什么和另一个人说了什么的传闻:按位复制肯定总是浅层复制。成员复制通常是深度复制,但你可能会搞砸它,所以你可能认为你正在进行深度复制,而实际上你并没有。正确的成员复制依赖于拥有适当的复制构造函数。
最后:
默认的复制构造函数会在对象被知道是平凡可复制时进行按位复制,或者在不可复制时进行成员复制。然而,编译器并不总是具有足够的信息来执行每个成员的正确复制。例如,指针通过复制指针本身而不是指向的对象来被复制。这就是为什么当你的对象不是平凡可复制时,通常不应该依赖编译器提供默认的复制构造函数。
用户提供的构造函数可以执行用户想要的任何类型的复制。希望用户明智选择并进行成员复制。
std::is_trivially_copyable
旨在确定何时浅复制有效,特别是何时可以使用memcpy()
。 - Persixtystruct 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
memcpy()
复制内存中的对象时,您将得到 Bitwise Copy。结果可能是语义上的 Deep Copy,或者(更可能)是 Shallow Copy(如果涉及指针),或者可能仅仅是一堆不再是从复制源类型的有效对象形式的位。非联合类 X 的隐式定义复制/移动构造函数对其基类和成员执行成员逐一复制/移动。 (...)
在[class.copy.ctor]
中描述了这意味着什么:
每个基类或非静态数据成员都以适合其类型的方式进行复制/移动:(...)
(!!) 对象可以根据情况 语义上等同于 上述所有 3 种类型的 Memberwise Copy。