这个问题的答案是社区的努力。编辑现有答案以改进这篇文章。它目前不接受新的答案或交互。
深拷贝和浅拷贝有什么区别?
广度与深度;考虑以您的对象为根节点的引用树。
浅的:
https://i.stack.imgur.com/49psq.png
变量 A 和 B 指代不同的内存区域,当 B 分配给 A 时,这两个变量指代相同的内存区域。以后对其中一个内容的修改会立即反映在另一个内容中,因为它们共享内容。
深的:
https://i.stack.imgur.com/DRLn7.png
变量 A 和 B 指的是不同的内存区域,当 B 分配给 A 时,A 指向的内存区域中的值被复制到 B 指向的内存区域中。以后对内容的修改对于 A 或 B 仍然是唯一的;内容不共享。
浅拷贝尽可能少地重复。集合的浅拷贝是集合结构的副本,而不是元素的副本。使用浅拷贝,两个集合现在共享单个元素。
深拷贝复制一切。集合的深层副本是两个集合,其中原始集合中的所有元素都重复。
Address
对象的 Customer
对象,则“逐位”复制 Customer
对象意味着复制到 Address
对象的 指针/引用 .原始和副本都指向同一个 Address
对象,而深层副本将创建一个新的 Address
对象并指向该对象。
简而言之,这取决于什么指向什么。在浅拷贝中,对象 B 指向对象 A 在内存中的位置。在深拷贝中,对象 A 的内存位置中的所有内容都被复制到对象 B 的内存位置。
这篇 wiki 文章有一个很棒的图表。
http://en.wikipedia.org/wiki/Object_copy
特别是对于 iOS 开发者:
如果 B
是 A
的浅拷贝,那么对于原始数据,它类似于 B = [A assign];
,而对于对象,它类似于 B = [A retain]
;
B 和 A 指向同一个内存位置
如果 B
是 A
的深拷贝,那么它就像 B = [A copy];
B 和 A 指向不同的内存位置
内存地址与 A 相同
与 A 的内容相同
浅拷贝:将成员值从一个对象复制到另一个对象。
深度复制:将成员值从一个对象复制到另一个对象。任何指针对象都会被复制和深度复制。
例子:
class String
{
int size;
char* data;
};
String s1("Ace"); // s1.size = 3 s1.data=0x0000F000
String s2 = shallowCopy(s1);
// s2.size =3 s2.data = 0X0000F000
String s3 = deepCopy(s1);
// s3.size =3 s3.data = 0x0000F00F
// (With Ace copied to this location.)
只是为了便于理解,您可以阅读这篇文章:https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
浅拷贝:
https://i.stack.imgur.com/nJJ6K.gif
深拷贝:
https://i.stack.imgur.com/R6Jlg.gif
我在这里没有看到一个简短易懂的答案——所以我会试一试。
对于浅拷贝,源指向的任何对象也被目标指向(因此不会复制引用的对象)。
对于深拷贝,源指向的任何对象都会被拷贝,而目标指向的拷贝会被拷贝(所以现在每个被引用的对象都有 2 个)。这会沿着对象树递归。
{想象两个对象:相同类型的 A 和 B _t(相对于 C++)并且您正在考虑将 A 浅/深复制到 B}
浅拷贝:简单地将 A 的引用复制到 B 中。将其视为 A 的地址的副本。因此,A 和 B 的地址将相同,即它们将指向相同的内存位置,即数据内容。
深拷贝:简单地对A的所有成员进行拷贝,在不同的位置为B分配内存,然后将拷贝的成员分配给B,实现深拷贝。这样,如果 A 变得不存在,B 在内存中仍然有效。使用的正确术语是克隆,您知道它们完全相同,但又不同(即在内存空间中存储为两个不同的实体)。您还可以提供克隆包装器,您可以在其中通过包含/排除列表决定在深度复制期间选择哪些属性。当您创建 API 时,这是很常见的做法。
仅当您了解所涉及的风险时,您才可以选择进行浅拷贝。当您在 C++ 或 C 中处理大量指针时,对对象进行浅拷贝真的是个坏主意。
示例_OF_DEEP COPY_ 一个示例是,当您尝试进行图像处理和对象识别时,您需要将“不相关和重复运动”从您的处理区域中屏蔽掉。如果您使用的是图像指针,那么您可能有保存这些蒙版图像的规范。现在...如果您对图像进行浅拷贝,当指针引用从堆栈中被 KILLED 时,您会丢失引用及其副本,即在某些时候会出现访问冲突的运行时错误。在这种情况下,您需要的是通过 CLONING 获得图像的深层副本。通过这种方式,您可以检索掩码以备将来需要时使用。
示例_OF_SHALLOW_COPY 与 StackOverflow 中的用户相比,我的知识不是很丰富,所以请随意删除这部分并举一个很好的例子,如果你能澄清的话。但我真的认为,如果您知道您的程序将运行无限时间,即通过函数调用在堆栈上连续“push-pop”操作,那么做浅拷贝不是一个好主意。如果您正在向业余或新手演示某些东西(例如 C/C++ 教程的东西),那么它可能没问题。但是,如果您正在运行诸如监视和检测系统或声纳跟踪系统之类的应用程序,则不应该对对象进行浅层复制,因为它迟早会杀死您的程序。
char * Source = "Hello, world.";
char * ShallowCopy = Source;
char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);
“ShallowCopy”指向与“Source”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。
什么是浅拷贝?
https://i.stack.imgur.com/LnDQh.png
在该图中,MainObject1
具有 int 类型的字段 field1
和 ContainObject
类型的 ContainObject1
。当您对 MainObject1
进行浅拷贝时,会使用 field2
创建 MainObject2
,其中包含 field1
的复制值,并且仍然指向 ContainObject1
本身。请注意,由于 field1
是原始类型,它的值被复制到 field2
,但由于 ContainedObject1
是一个对象,所以 MainObject2
仍然指向 ContainObject1
。因此,对 MainObject1
中的 ContainObject1
所做的任何更改都将反映在 MainObject2
中。
现在如果这是浅拷贝,让我们看看什么是深拷贝?
什么是深拷贝?
https://i.stack.imgur.com/kocda.png
在该图中,MainObject1 具有 int 类型的字段 field1
和 ContainObject
类型的 ContainObject1
。当您对 MainObject1
进行深层复制时,会创建 MainObject2
,其中 field2
包含 field1
的复制值,ContainObject2
包含 ContainObject1
的复制值。请注意,对 MainObject1
中的 ContainObject1
所做的任何更改都不会反映在 MainObject2
中。
field3
,当它能够尝试理解像那个问题一样深的东西时,那个例子中的 #3 在哪里发生 ContainObject2
?
在面向对象编程中,类型包括成员字段的集合。这些字段可以按值或按引用(即指向值的指针)存储。
在浅拷贝中,创建该类型的新实例并将值复制到新实例中。引用指针也像值一样被复制。因此,引用指向原始对象。对通过引用存储的成员的任何更改都会出现在原始和副本中,因为没有对引用的对象进行复制。
在深拷贝中,按值存储的字段像以前一样被复制,但指向按引用存储的对象的指针不会被复制。取而代之的是,对引用的对象进行深层复制,并存储指向新对象的指针。对这些引用对象所做的任何更改都不会影响该对象的其他副本。
深拷贝
深拷贝复制所有字段,并复制字段指向的动态分配内存。当一个对象连同它所引用的对象一起被复制时,就会发生深拷贝。
浅拷贝
浅拷贝是对象的按位拷贝。创建一个新对象,该对象具有原始对象中值的精确副本。如果对象的任何字段是对其他对象的引用,则仅复制引用地址,即仅复制内存地址。
“ShallowCopy”指向与“Source”相同的内存位置。 “DeepCopy”指向内存中的不同位置,但内容相同。
我想举个例子而不是正式的定义。
var originalObject = {
a : 1,
b : 2,
c : 3,
};
这段代码显示了一个浅拷贝:
var copyObject1 = originalObject;
console.log(copyObject1.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject1.a = 4;
console.log(copyObject1.a); //now it will print 4
console.log(originalObject.a); // now it will also print 4
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // now it will print 1
这段代码显示了一个深拷贝:
var copyObject2 = Object.assign({}, originalObject);
console.log(copyObject2.a); // it will print 1
console.log(originalObject.a); // it will also print 1
copyObject2.a = 4;
console.log(copyObject2.a); // now it will print 4
console.log(originalObject.a); // !! now it will print 1 !!
1 1 4 4 4 4 4 4
Source deep ${source.c.age}
) source.c.age = 3 console.log(dCopy.c.age) console.log({ 1})
浅克隆:定义:“对象的浅拷贝复制‘主’对象,但不复制内部对象。”当自定义对象(例如 Employee)只有原始的字符串类型变量时,您可以使用浅克隆。
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
您在覆盖的 clone() 方法中返回 super.clone();
,您的工作就结束了。
深度克隆:定义:“与浅拷贝不同,深度拷贝是对象的完全独立拷贝。”表示当一个 Employee 对象持有另一个自定义对象时:
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
然后您必须编写代码来克隆“地址”对象以及覆盖的 clone() 方法。否则,Address 对象将不会克隆,并且当您更改克隆的 Employee 对象中的 Address 值时会导致错误,这也反映了原始对象。
var source = { firstName="Jane", lastname="Jones" };
var shallow = ShallowCopyOf(source);
var deep = DeepCopyOf(source);
source.lastName = "Smith";
WriteLine(source.lastName); // prints Smith
WriteLine(shallow.lastName); // prints Smith
WriteLine(deep.lastName); // prints Jones
浅复制 - 原始和浅复制对象内的引用变量具有对公共对象的引用。
深度复制 - 原始对象和深度复制对象内的引用变量引用了不同的对象。
克隆总是做浅拷贝。
public class Language implements Cloneable{
String name;
public Language(String name){
this.name=name;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
主要课程如下 -
public static void main(String args[]) throws ClassNotFoundException, CloneNotSupportedException{
ArrayList<Language> list=new ArrayList<Language>();
list.add(new Language("C"));
list.add(new Language("JAVA"));
ArrayList<Language> shallow=(ArrayList<Language>) list.clone();
//We used here clone since this always shallow copied.
System.out.println(list==shallow);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==shallow.get(i));//true
ArrayList<Language> deep=new ArrayList<Language>();
for(Language language:list){
deep.add((Language) language.clone());
}
System.out.println(list==deep);
for(int i=0;i<list.size();i++)
System.out.println(list.get(i)==deep.get(i));//false
}
以上的输出将是-
假 真 真 假 假 假
对原始对象所做的任何更改都将反映在浅对象而不是深对象中。
list.get(0).name="ViSuaLBaSiC";
System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName());
输出 - ViSuaLBaSiC C
想象一下,有两个数组称为 arr1 和 arr2。
arr1 = arr2; //shallow copy
arr1 = arr2.clone(); //deep copy
简单来说,浅拷贝类似于按引用调用,深拷贝类似于按值调用
在引用调用中,函数的形参和实参都引用相同的内存位置和值。
在按值调用中,函数的形参和实参都指不同的内存位置但具有相同的值。
浅拷贝构造一个新的复合对象并将其引用插入到原始对象中。
与浅拷贝不同,深拷贝构造新的复合对象并插入原始复合对象的原始对象的副本。
让我们举个例子。
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
上面的代码打印 FALSE。
让我们看看如何。
原始复合对象 x=[1,[2]]
(称为复合对象,因为它在对象内部有对象 (Inception))
https://i.stack.imgur.com/QtsjW.png
正如您在图像中看到的,列表中有一个列表。
然后我们使用 y = copy.copy(x)
创建它的浅表副本。 python在这里所做的是,它将创建一个新的复合对象,但其中的对象指向原始对象。
https://i.stack.imgur.com/QLeDN.png
在图像中,它为外部列表创建了一个新副本。但内部列表与原始列表相同。
现在我们使用 z = copy.deepcopy(x)
创建它的深拷贝。 python在这里所做的是,它将为外部列表和内部列表创建新对象。如下图所示(红色突出显示)。
https://i.stack.imgur.com/4fYHO.png
最后代码打印 False
,因为 y 和 z 不是同一个对象。
HTH。
struct sample
{
char * ptr;
}
void shallowcpy(sample & dest, sample & src)
{
dest.ptr=src.ptr;
}
void deepcpy(sample & dest, sample & src)
{
dest.ptr=malloc(strlen(src.ptr)+1);
memcpy(dest.ptr,src.ptr);
}
要为其他答案添加更多内容,
对象的浅拷贝对基于值类型的属性执行按值复制,对基于引用类型的属性执行按引用复制。
对象的深层复制对基于值类型的属性执行按值复制,以及对层次结构深处(引用类型的)基于引用类型的属性执行按值复制
浅拷贝不会创建新引用,但深拷贝会创建新引用。
这是解释深拷贝和浅拷贝的程序。
public class DeepAndShollowCopy {
int id;
String name;
List<String> testlist = new ArrayList<>();
/*
// To performing Shallow Copy
// Note: Here we are not creating any references.
public DeepAndShollowCopy(int id, String name, List<String>testlist)
{
System.out.println("Shallow Copy for Object initialization");
this.id = id;
this.name = name;
this.testlist = testlist;
}
*/
// To performing Deep Copy
// Note: Here we are creating one references( Al arraylist object ).
public DeepAndShollowCopy(int id, String name, List<String> testlist) {
System.out.println("Deep Copy for Object initialization");
this.id = id;
this.name = name;
String item;
List<String> Al = new ArrayList<>();
Iterator<String> itr = testlist.iterator();
while (itr.hasNext()) {
item = itr.next();
Al.add(item);
}
this.testlist = Al;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Oracle");
list.add("C++");
DeepAndShollowCopy copy=new DeepAndShollowCopy(10,"Testing", list);
System.out.println(copy.toString());
}
@Override
public String toString() {
return "DeepAndShollowCopy [id=" + id + ", name=" + name + ", testlist=" + testlist + "]";
}
}
摘自 [博客]:http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
深拷贝涉及使用一个对象的内容来创建同一类的另一个实例。在深拷贝中,两个对象可能包含相同的信息,但目标对象将拥有自己的缓冲区和资源。任何一个对象的破坏都不会影响剩余的对象。重载的赋值运算符将创建对象的深层副本。
浅拷贝涉及将一个对象的内容复制到同一类的另一个实例中,从而创建镜像。由于引用和指针的直接复制,两个对象将共享另一个对象的相同外部包含的内容,这是不可预测的。
解释:
使用复制构造函数,我们只需逐个复制数据值。这种复制方法称为浅拷贝。如果对象是一个简单的类,由内置类型和没有指针组成,这是可以接受的。这个函数将使用值和对象,并且它的行为不会被浅拷贝改变,只有作为成员的指针的地址被复制,而不是地址指向的值。然后对象的数据值会被函数无意中更改。当函数超出范围时,对象的副本及其所有数据都会从堆栈中弹出。
如果对象有任何指针,则需要执行深拷贝。使用对象的深层副本,为自由存储中的对象分配内存并复制指向的元素。深层副本用于从函数返回的对象。
我从以下几行中了解到。
浅拷贝将对象值类型(int、float、bool)字段复制到目标对象中,并且对象的引用类型(字符串、类等)被复制为目标对象中的引用。在这个目标中,引用类型将指向源对象的内存位置。
深拷贝将对象的值和引用类型复制到目标对象的全新副本中。这意味着值类型和引用类型都将被分配一个新的内存位置。
浅拷贝是创建一个新对象,然后将当前对象的非静态字段复制到新对象中。如果一个字段是一个值类型 --> 对该字段进行逐位复制;对于引用类型 --> 引用被复制但被引用的对象不是;因此原始对象及其克隆引用同一个对象。
深拷贝是创建一个新对象,然后将当前对象的非静态字段复制到新对象。如果字段是值类型 --> 将执行该字段的逐位复制。如果字段是引用类型--> 执行引用对象的新副本。要克隆的类必须标记为 [Serializable]。
复制数组:
Array 是一个类,这意味着它是引用类型,因此 array1 = array2 导致两个变量引用同一个数组。
但是看看这个例子:
static void Main()
{
int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
int[] arr2 = new int[] { 6, 7, 8, 9, 0 };
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = arr1;
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = (int[])arr1.Clone();
arr1[2] = 12;
Console.WriteLine(arr1[2] + " " + arr2[2]);
}
浅克隆意味着只复制克隆数组所代表的内存。
如果数组包含值类型对象,则复制值;
如果数组包含引用类型,则仅复制引用 - 因此有两个数组的成员引用相同的对象。
要创建深层副本——引用类型重复,您必须遍历数组并手动克隆每个元素。
private void button1_Click(object sender, EventArgs e) { int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{6,7,8,9,0}; MessageBox.Show(arr1[2] + " " + arr2[2]); arr2 = arr1; MessageBox.Show(arr1[2] + " " + arr2[2]); arr1[2] = 12; MessageBox.Show(arr1[2] + " " + arr2[2]); }
复制构造函数用于用先前创建的同一类的对象来初始化新对象。默认情况下,编译器写了一个浅拷贝。当不涉及动态内存分配时,浅拷贝工作正常,因为当涉及动态内存分配时,两个对象将指向堆中的相同内存位置,因此为了解决这个问题,我们编写了深拷贝,因此两个对象都有自己的属性副本记忆中。要阅读包含完整示例和说明的详细信息,您可以查看文章 C++ constructors。
为了在浅拷贝和简单地为列表分配一个新的变量名之间增加一点混淆。
“假设我们有:
x = [
[1,2,3],
[4,5,6],
]
此语句创建 3 个列表:2 个内部列表和一个外部列表。然后以名称 x 提供对外部列表的引用。如果我们这样做
y = x
没有数据被复制。我们在内存中的某个地方仍然有相同的 3 个列表。所有这一切都是使外部列表在名称 y 下可用,除了它以前的名称 x。如果我们这样做
y = list(x)
或者
y = x[:]
这将创建一个与 x 具有相同内容的新列表。列表 x 包含对 2 个内部列表的引用,因此新列表还将包含对相同的 2 个内部列表的引用。只复制一个列表——外部列表。现在内存中有4个列表,两个内部列表,外部列表和外部列表的副本。原始外部列表在名称 x 下可用,而新外部列表在名称 y 下可用。
内部列表没有被复制!此时您可以从 x 或 y 访问和编辑内部列表!
如果您有一个二维(或更高)列表,或任何类型的嵌套数据结构,并且您想要制作所有内容的完整副本,那么您想要使用复制模块中的 deepcopy() 函数。您的解决方案也适用于二维列表,因为迭代外部列表中的项目并制作每个项目的副本,然后为所有内部副本构建一个新的外部列表。”