Каква е разликата между дълбоко копие и плитко копие?
Каква е разликата между дълбоко копие и плитко копие?
Отговори (30)
Плитките копия се дублират възможно най-малко. Плиткото копие на колекция е копие на структурата на колекцията, а не на елементите. С плитко копие две колекции вече споделят отделните елементи.
Дълбоките копия дублират всичко. Дълбоко копие на колекция представлява две колекции с дублирани всички елементи в оригиналната колекция.
int[]
в Java например, елементите наистина ще бъдат копирани.
- person aioobe; 19.03.2018
Customer
, който има обект Address
, копирането на обекта Customer
малко по малко означава, че указателят/препратката към обекта Address
се копира. И оригиналът, и копието сочат към един и същ Address
обект, докато дълбокото копие ще създаде нов Address
обект и ще сочи към него вместо това.
- person Raphael Schmitz; 21.02.2019
Широчина срещу Дълбочина; мислете от гледна точка на дърво от препратки с вашия обект като основен възел.
Плитко:
Променливите A и B се отнасят за различни области на паметта, когато B е присвоена на A, двете променливи се отнасят за една и съща област на паметта. По-късните модификации на съдържанието на един от тях незабавно се отразяват в съдържанието на други, тъй като те споделят съдържание.
Дълбок:
Променливите A и B се отнасят до различни области на паметта, когато B е присвоена на A, стойностите в областта на паметта, към която A сочи, се копират в областта на паметта, към която B сочи. По-късните модификации на съдържанието на или остават уникални за A или B; съдържанието не се споделя.
Накратко, зависи какво сочи към какво. В плитко копие обект B сочи към местоположението на обект A в паметта. При дълбоко копиране всички неща в местоположението на паметта на обект A се копират в местоположението на паметта на обект B.
Тази статия в wiki има страхотна диаграма.
http://en.wikipedia.org/wiki/Object_copy
Опитайте се да разгледате следното изображение
Например Object.MemberwiseClone създава плитко копие връзка
и с помощта на интерфейса ICloneable можете да получите задълбочено копие, както е описано тук
Специално за разработчици на iOS:
Ако B
е плитко копие на A
, тогава за примитивни данни е като B = [A assign];
, а за обекти е като B = [A retain]
;
B и A сочат към едно и също място в паметта
Ако B
е задълбочено копие на A
, то е като B = [A copy];
B и A сочат към различни места в паметта
Адресът на паметта B е същият като този на 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
Плитко копие:
Дълбоко копие:
Не съм виждал кратък, лесен за разбиране отговор тук - така че ще опитам.
С плитко копие всеки обект, към който сочи източникът, също се сочи към дестинацията (така че да не се копират обекти, към които има препратки).
С дълбоко копие всеки обект, към който сочи източникът, се копира и към копието се сочи дестинацията (така че сега ще има 2 от всеки рефериран обект). Това рекурсира надолу по дървото на обектите.
{Представете си два обекта: A и B от същия тип _t (по отношение на C++) и мислите за плитко/дълбоко копиране на A към B}
Плитко копие: Просто правете копие на препратката към A в B. Мислете за това като за копие на адреса на A. Така че адресите на A и B ще бъдат еднакви, т.е. те ще сочат към едно и също място в паметта, т.е. съдържанието на данните.
Дълбоко копие: Просто правете копие на всички членове на A, разпределяте памет на друго място за B и след това присвоявате копираните членове на B, за да постигнете дълбоко копие. По този начин, ако A стане несъществуващо, B все още е валидно в паметта. Правилният термин за използване би бил клониране, където знаете, че и двете са напълно еднакви, но все пак различни (т.е. съхранени като две различни единици в пространството на паметта). Можете също да предоставите вашата обвивка на клонинг, където можете да решите чрез списък за включване/изключване кои свойства да изберете по време на дълбоко копиране. Това е доста често срещана практика, когато създавате API.
Можете да изберете да направите плитко копие САМО_АКО разбирате заложените залози. Когато имате огромен брой указатели, с които трябва да се справите в C++ или C, правенето на плитко копие на обект е НАИСТИНА лоша идея.
EXAMPLE_OF_DEEP COPY_ Например, когато се опитвате да извършите обработка на изображения и разпознаване на обекти, трябва да маскирате „Неуместно и повтарящо се движение“ от вашите зони за обработка. Ако използвате указатели на изображения, тогава може да имате спецификацията да запазите тези маскирани изображения. СЕГА... ако направите плитко копие на изображението, когато препратките към указателя са УБИТИ от стека, вие сте загубили препратката и нейното копие, т.е. ще има грешка по време на изпълнение на нарушение на достъпа в даден момент. В този случай това, от което се нуждаете, е дълбоко копие на вашето изображение, като го КЛОНИРАТЕ. По този начин можете да извлечете маските, в случай че ви потрябват в бъдеще.
EXAMPLE_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“ сочи към различно място в паметта, но съдържанието е същото.
Какво е плитко копиране?
Плиткото копие е побитово копие на обект. Създава се нов обект, който има точно копие на стойностите в оригиналния обект. Ако някое от полетата на обекта е препратка към други обекти, само референтните адреси се копират, т.е. само адресът на паметта се копира.
На тази фигура MainObject1
има полета field1
от тип int и ContainObject1
от тип ContainObject
. Когато направите плитко копие на MainObject1
, MainObject2
се създава с field2
, съдържащо копираната стойност на field1
и все още сочещо към самото ContainObject1
. Имайте предвид, че тъй като field1
е от примитивен тип, неговата стойност се копира в field2
, но тъй като ContainedObject1
е обект, MainObject2
все още сочи към ContainObject1
. Така че всички промени, направени в ContainObject1
в MainObject1
, ще бъдат отразени в MainObject2
.
Сега, ако това е плитко копие, нека да видим какво е дълбоко копие?
Какво е дълбоко копиране?
Дълбокото копие копира всички полета и прави копия на динамично разпределената памет, посочена от полетата. Дълбоко копие възниква, когато даден обект се копира заедно с обектите, към които се отнася.
На тази фигура MainObject1 има полета field1
от тип int и ContainObject1
от тип ContainObject
. Когато правите дълбоко копие на MainObject1
, MainObject2
се създава с field2
, съдържащо копираната стойност на field1
и ContainObject2
, съдържащо копираната стойност на ContainObject1
. Имайте предвид, че всички промени, направени в ContainObject1
в MainObject1
, няма да се отразят в MainObject2
.
field3
, което, когато е в състояние да се опита да разбере нещо толкова дълбоко като този проблем, къде се случва това #3 в този пример ContainObject2
?
- person Robb_2015; 29.12.2015
В обектно-ориентираното програмиране типът включва колекция от полета-членове. Тези полета могат да се съхраняват или по стойност, или по препратка (т.е. указател към стойност).
В плитко копие се създава нов екземпляр от типа и стойностите се копират в новия екземпляр. Референтните указатели също се копират точно като стойностите. Следователно препратките сочат към оригиналните обекти. Всички промени в членовете, които се съхраняват чрез препратка, се появяват както в оригинала, така и в копието, тъй като не е направено копие на посочения обект.
В дълбоко копие полетата, които се съхраняват по стойност, се копират както преди, но указателите към обекти, съхранявани по препратка, не се копират. Вместо това се прави дълбоко копие на посочения обект и се съхранява указател към новия обект. Всички промени, които се правят в тези референтни обекти, няма да засегнат други копия на обекта.
„ShallowCopy“ сочи към същото място в паметта като „Source“. „DeepCopy“ сочи към различно място в паметта, но съдържанието е същото.
Плитко клониране:
Определение: „Плитко копие на обект копира „основния“ обект, но не копира вътрешните обекти.“ Когато персонализиран обект (напр. Служител) има само примитивни променливи от тип String, тогава използвате плитко клониране.
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
Връщате super.clone();
в отменения метод clone() и работата ви приключва.
Дълбоко клониране:
Определение: „За разлика от плиткото копие, дълбокото копие е напълно независимо копие на обект.“
Означава, че обект Employee съдържа друг потребителски обект:
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
След това трябва да напишете кода, за да клонирате и обекта „Адрес“ в заменения метод clone(). В противен случай обектът Address няма да се клонира и причинява грешка, когато промените стойността на Address в клониран обект Employee, който отразява и оригиналния.
Бих искал да дам пример, а не формалната дефиниция.
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
- person Suresh Prajapati; 11.08.2019
Source deep ${source.c.age}
) source.c.age = 3 console.log(dCopy.c.age) console.log(Source deep ${source.c.age}
)
- person Swarup Chavan; 29.07.2020
Дълбоко копиране
Дълбокото копие копира всички полета и прави копия на динамично разпределената памет, посочена от полетата. Дълбоко копие възниква, когато даден обект се копира заедно с обектите, към които се отнася.
Плитко копие
Плиткото копие е побитово копие на обект. Създава се нов обект, който има точно копие на стойностите в оригиналния обект. Ако някое от полетата на обекта е препратка към други обекти, само референтните адреси се копират, т.е. само адресът на паметта се копира.
Плитко копие- Референтната променлива в оригиналните и плитко копирани обекти има препратка към общ обект.
Дълбоко копие- Референтната променлива в оригиналните и дълбоко копирани обекти има препратка към различен обект.
клонирането винаги прави плитко копиране.
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());
OutPut- 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]]
(наречен като съставен, защото има обект вътре в обекта (начало))
както можете да видите на изображението, има списък в списъка.
След това създаваме негово плитко копие с помощта на y = copy.copy(x)
. Това, което Python прави тук, е, че ще създаде нов съставен обект, но обектите вътре в тях сочат към оригиналните обекти.
В изображението е създадено ново копие за външен списък. но вътрешният списък остава същият като оригиналния.
Сега създаваме дълбоко копие на него с помощта на z = copy.deepcopy(x)
. това, което python прави тук, е, че ще създаде нов обект за външен списък, както и за вътрешен списък. както е показано на изображението по-долу (маркирано в червено).
В края кодът отпечатва False
, тъй като y и z не са едни и същи обекти.
HTH.
За да добавите още към други отговори,
- плитко копие на обект изпълнява копиране по стойност за свойства, базирани на типове стойности, и копиране по препратка за свойства, базирани на референтни типове.
- Дълбоко копие на обект извършва копиране по стойност за свойства, базирани на типове стойности, както и копиране по стойност за свойства, базирани на референтни типове, дълбоко в йерархията (на референтни типове)
Плиткото копиране е създаване на нов обект и след това копиране на нестатичните полета на текущия обект в новия обект. Ако дадено поле е тип стойност --> се изпълнява битово копие на полето; за референтен тип --> препратката се копира, но препратеният обект не е; следователно оригиналният обект и неговият клонинг се отнасят за един и същ обект.
Дълбокото копиране създава нов обект и след това копира нестатичните полета на текущия обект в новия обект. Ако дадено поле е тип стойност --> се изпълнява битово копие на полето. Ако дадено поле е референтен тип --> се изпълнява ново копие на препоръчания обект. Класовете, които ще бъдат клонирани, трябва да бъдат маркирани като [Serializable].
Взето от [блог]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
Дълбоко копиране включва използването на съдържанието на един обект за създаване на друг екземпляр от същия клас. В дълбоко копие двата обекта може да съдържат една и съща информация, но целевият обект ще има свои собствени буфери и ресурси. унищожаването на който и да е обект няма да засегне останалия обект. Претовареният оператор за присвояване ще създаде дълбоко копие на обекти.
Плиткото копиране включва копиране на съдържанието на един обект в друг екземпляр от същия клас, като по този начин се създава огледален образ. Благодарение на директното копиране на препратки и указатели, двата обекта ще споделят едно и също външно съдържание на другия обект, за да бъдат непредвидими.
Обяснение:
С помощта на конструктор за копиране ние просто копираме стойностите на данните член по член. Този метод на копиране се нарича плитко копиране. Ако обектът е прост клас, състоящ се от вградени типове и без указатели, това би било приемливо. Тази функция ще използва стойностите и обектите и нейното поведение няма да бъде променено с плитко копие, копират се само адресите на указателите, които са членове, а не стойността, към която сочи адресът. След това стойностите на данните на обекта биха били неволно променени от функцията. Когато функцията излезе извън обхвата, копието на обекта с всичките му данни се изважда от стека.
Ако обектът има указатели, трябва да се изпълни дълбоко копие. С дълбокото копие на обект се разпределя памет за обекта в свободното хранилище и посочените елементи се копират. Задълбочено копие се използва за обекти, които се връщат от функция.
Разбрах от следващите редове.
Плиткото копиране копира тип стойност(int, float, bool) полета в целевия обект и референтните типове на обекта (низ, клас и т.н.) се копират като препратки в целевия обект . В тази целева препратка типовете ще сочат към местоположението на паметта на изходния обект.
Дълбокото копиране копира стойността на обекта и референтните типове в пълно ново копие на целевите обекти. Това означава, че както стойностните типове, така и референтните типове ще бъдат разпределени на нови места в паметта.
Плиткото копие няма да създаде нова препратка, но дълбокото копие ще създаде новата препратка.
Ето програмата за обяснение на дълбокото и плитко копие.
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 + "]";
}
}
Копиране на ararys:
Масивът е клас, което означава, че е референтен тип, така че 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]); }
- person DeanOC; 20.12.2012
Като добавим към всички по-горе дефиниции, още едно и най-често използвано дълбоко копие е в конструктора за копиране (или оператора за претоварване на присвояването) на класа.
Плитко копиране --> е, когато не предоставяте конструктор за копиране. Тук само обектът се копира, но не всички членове на класа се копират.
Дълбоко копиране --> е, когато сте решили да внедрите конструктор за копиране или присвояване на претоварване във вашия клас и позволява копиране на всички членове на класа.
MyClass& MyClass(const MyClass& obj) // copy constructor for MyClass
{
// write your code, to copy all the members and return the new object
}
MyClass& operator=(const MyClass& obj) // overloading assignment operator,
{
// write your code, to copy all the members and return the new object
}
Конструкторът за копиране се използва за инициализиране на новия обект с предварително създадения обект от същия клас. По подразбиране компилаторът написа плитко копие. Плиткото копиране работи добре, когато не е включено динамично разпределение на паметта, защото когато е включено динамично разпределение на памет, тогава и двата обекта ще сочат към едно и също място в паметта в купчина. Следователно, за да премахнем този проблем, ние написахме дълбоко копие, така че и двата обекта да имат собствено копие на атрибути в спомен. За да прочетете подробностите с пълни примери и обяснения, можете да видите статията C++ конструктори .
За да добавите още малко за объркване между плитко копиране и просто присвояване на ново име на променлива към списъка.
„Кажете, че имаме:
x = [
[1,2,3],
[4,5,6],
]
Този оператор създава 3 списъка: 2 вътрешни списъка и един външен списък. След това се предоставя препратка към външния списък под името x. Ако го направим
y = x
никакви данни не се копират. Все още имаме същите 3 списъка в паметта някъде. Всичко, което направи, е да направи външния списък достъпен под името y, в допълнение към предишното му име x. Ако го направим
y = list(x)
or
y = x[:]
Това създава нов списък със същото съдържание като x. Списък x съдържа препратка към 2 вътрешни списъка, така че новият списък също ще съдържа препратка към същите тези 2 вътрешни списъка. Копира се само един списък – външният списък. Сега има 4 списъка в паметта, двата вътрешни списъка, външния списък и копието на външния списък. Оригиналният външен списък е достъпен под името x, а новият външен списък е достъпен под името y.
Вътрешните списъци не са копирани! Можете да получите достъп и да редактирате вътрешните списъци от x или y в този момент!
Ако имате двуизмерен (или по-висок) списък или някакъв вид вложена структура от данни и искате да направите пълно копие на всичко, тогава искате да използвате функцията deepcopy() в модула за копиране. Вашето решение работи и за 2-D списъци, тъй като итерира елементите във външния списък и прави копие на всеки от тях, след което изгражда нов външен списък за всички вътрешни копия."