Qual è la differenza tra una copia profonda e una copia superficiale?
Qual è la differenza tra una copia profonda e una copia superficiale?
Risposte:
Copie poco profonde duplicate il meno possibile. Una copia superficiale di una raccolta è una copia della struttura della raccolta, non degli elementi. Con una copia superficiale, due raccolte ora condividono i singoli elementi.
Le copie profonde duplicano tutto. Una copia profonda di una raccolta sono due raccolte con tutti gli elementi della raccolta originale duplicati.
Larghezza vs Profondità; pensa in termini di un albero di riferimenti con il tuo oggetto come nodo radice.
Shallow:
Le variabili A e B si riferiscono a diverse aree di memoria, quando B è assegnato ad A le due variabili si riferiscono alla stessa area di memoria. Modifiche successive al contenuto di uno dei due si riflettono istantaneamente nel contenuto dell'altro, poiché condividono il contenuto.
In profondità:
Le variabili A e B si riferiscono a diverse aree della memoria, quando B è assegnato ad A i valori nell'area di memoria in cui A punta sono copiati nell'area di memoria in cui B indica. Modifiche successive al contenuto di entrambi rimangono uniche per A o B; i contenuti non sono condivisi.
In breve, dipende da cosa indica cosa. In una copia superficiale, l'oggetto B indica la posizione dell'oggetto A in memoria. Nella copia profonda, tutte le cose nella posizione di memoria dell'oggetto A vengono copiate nella posizione di memoria dell'oggetto B.
Questo articolo wiki ha un diagramma fantastico.
Prova a considerare la seguente immagine
Ad esempio Object.MemberwiseClone crea un collegamento di copia superficiale
e usando l' interfaccia ICloneable puoi ottenere una copia approfondita come descritto qui
Soprattutto per gli sviluppatori iOS:
Se B
è una copia superficiale di A
, allora per i dati primitivi è simile B = [A assign];
e per gli oggetti è simile B = [A retain]
;
B e A indicano la stessa posizione di memoria
Se B
è una copia profonda di A
, allora è comeB = [A copy];
B e A indicano diverse posizioni di memoria
L'indirizzo di memoria B è uguale a quello di A.
B ha gli stessi contenuti di A
Copia superficiale: copia i valori dei membri da un oggetto a un altro.
Deep Copy: copia i valori dei membri da un oggetto a un altro.
Tutti gli oggetti puntatore vengono duplicati e copiati in profondità.
Esempio:
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.)
Non ho visto una risposta breve e facile da capire qui - quindi ci proverò.
Con una copia superficiale, qualsiasi oggetto a cui punta l'origine viene anche indicato dalla destinazione (in modo che nessun oggetto referenziato venga copiato).
Con una copia profonda, qualsiasi oggetto a cui punta l'origine viene copiato e la copia viene puntata dalla destinazione (quindi ora ci saranno 2 di ogni oggetto a cui viene fatto riferimento). Ciò ricorre verso il basso l'albero degli oggetti.
Solo per motivi di facile comprensione puoi seguire questo articolo: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
Copia superficiale:
Copia profonda:
{Immagina due oggetti: A e B dello stesso tipo _t (rispetto a C ++) e stai pensando di copiare superficialmente / in profondità da A a B}
Copia superficiale: crea semplicemente una copia del riferimento ad A in B. Pensa a questo come una copia dell'indirizzo di A. Pertanto, gli indirizzi di A e B saranno gli stessi, ovvero indicheranno la stessa posizione di memoria, ovvero il contenuto dei dati.
Copia profonda: crea semplicemente una copia di tutti i membri di A, alloca la memoria in una posizione diversa per B e quindi assegna i membri copiati a B per ottenere la copia profonda. In questo modo, se A diventa inesistente, B è ancora valido nella memoria. Il termine corretto da utilizzare sarebbe la clonazione, in cui si sa che entrambi sono totalmente uguali, ma tuttavia diversi (ovvero memorizzati come due entità diverse nello spazio di memoria). È inoltre possibile fornire il proprio wrapper clone in cui è possibile decidere tramite l'elenco di inclusione / esclusione quali proprietà selezionare durante la copia approfondita. Questa è una pratica abbastanza comune quando si creano API.
Puoi scegliere di fare una copia superficiale SOLO_IF hai capito la posta in gioco. Quando hai un numero enorme di puntatori da gestire in C ++ o C, fare una copia superficiale di un oggetto è DAVVERO una cattiva idea.
EXAMPLE_OF_DEEP COPY_ Un esempio è, quando si sta tentando di eseguire l'elaborazione delle immagini e il riconoscimento degli oggetti, è necessario mascherare "Movimento irrilevante e ripetitivo" dalle aree di elaborazione. Se si utilizzano i puntatori di immagini, è possibile che si disponga delle specifiche per salvare quelle immagini di maschere. ORA ... se si esegue una copia superficiale dell'immagine, quando i riferimenti del puntatore vengono KILLED dallo stack, si perde il riferimento e la sua copia, cioè ad un certo punto si verificherà un errore di runtime della violazione di accesso. In questo caso, ciò di cui hai bisogno è una copia profonda della tua immagine CLONANDO. In questo modo è possibile recuperare le maschere nel caso in cui siano necessarie in futuro.
EXAMPLE_OF_SHALLOW_COPY Non sono estremamente informato rispetto agli utenti di StackOverflow, quindi sentiti libero di eliminare questa parte e di dare un buon esempio se puoi chiarire. Ma penso davvero che non sia una buona idea fare una copia superficiale se sai che il tuo programma verrà eseguito per un periodo di tempo infinito, ad esempio un'operazione "push-pop" continua sullo stack con chiamate di funzione. Se stai dimostrando qualcosa a una persona dilettante o alle prime armi (ad esempio materiale didattico C / C ++), probabilmente va bene. Ma se stai eseguendo un'applicazione come il sistema di sorveglianza e rilevamento o Sonar Tracking System, non dovresti continuare a copiare superficialmente i tuoi oggetti perché prima o poi ucciderà il tuo programma.
char * Source = "Hello, world.";
char * ShallowCopy = Source;
char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);
'ShallowCopy' indica la stessa posizione in memoria di 'Fonte'. 'DeepCopy' indica una diversa posizione nella memoria, ma i contenuti sono gli stessi.
Che cos'è la copia superficiale?
La copia superficiale è una copia bit-saggia di un oggetto. Viene creato un nuovo oggetto che ha una copia esatta dei valori nell'oggetto originale. Se uno qualsiasi dei campi dell'oggetto è riferimento ad altri oggetti, vengono copiati solo gli indirizzi di riferimento, ovvero viene copiato solo l'indirizzo di memoria.
In questa figura, MainObject1
ha campi field1
di tipo int e ContainObject1
di tipo ContainObject
. Quando si esegue una copia superficiale di MainObject1
, MainObject2
viene creato field2
contenente il valore copiato di field1
e puntando ancora a ContainObject1
se stesso. Si noti che poiché field1
è di tipo primitivo, il suo valore viene copiato field2
ma poiché ContainedObject1
è un oggetto, MainObject2
punta ancora a ContainObject1
. Pertanto, qualsiasi modifica apportata a ContainObject1
in MainObject1
verrà riflessa in MainObject2
.
Ora, se questa è copia superficiale, vediamo cos'è la copia profonda?
Che cos'è Deep Copy?
Una copia profonda copia tutti i campi e crea copie della memoria allocata dinamicamente indicata dai campi. Una copia profonda si verifica quando un oggetto viene copiato insieme agli oggetti a cui si riferisce.
In questa figura, MainObject1 ha campi field1
di tipo int e ContainObject1
di tipo ContainObject
. Quando si esegue una copia profonda di MainObject1
, MainObject2
viene creato field2
contenente il valore copiato di field1
e ContainObject2
contenente il valore copiato di ContainObject1
. Si noti che eventuali modifiche apportate a ContainObject1
in MainObject1
non si riflettono in MainObject2
.
field3
che quando è in grado di provare a comprendere qualcosa di così profondo come quel problema, dove sta avvenendo quel numero 3 in quell'esempio ContainObject2
?
Nella programmazione orientata agli oggetti, un tipo include una raccolta di campi membri. Questi campi possono essere memorizzati per valore o per riferimento (ad esempio, un puntatore a un valore).
In una copia superficiale, viene creata una nuova istanza del tipo e i valori vengono copiati nella nuova istanza. Anche i puntatori di riferimento vengono copiati proprio come i valori. Pertanto, i riferimenti puntano agli oggetti originali. Eventuali modifiche ai membri archiviate per riferimento vengono visualizzate sia nell'originale che nella copia, poiché non è stata effettuata alcuna copia dell'oggetto a cui si fa riferimento.
In una copia profonda, i campi memorizzati per valore vengono copiati come prima, ma i puntatori agli oggetti memorizzati per riferimento non vengono copiati. Al contrario, viene eseguita una copia approfondita dell'oggetto a cui viene fatto riferimento e viene memorizzato un puntatore al nuovo oggetto. Eventuali modifiche apportate a tali oggetti referenziati non influiranno su altre copie dell'oggetto.
'ShallowCopy' indica la stessa posizione in memoria di 'Fonte'. 'DeepCopy' indica una diversa posizione nella memoria, ma i contenuti sono gli stessi.
Clonazione superficiale:
definizione: "Una copia superficiale di un oggetto copia l'oggetto" principale ", ma non copia gli oggetti interni." Quando un oggetto personalizzato (es. Employee) ha solo variabili primitive di tipo String, allora si usa la clonazione superficiale.
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
Si ritorna super.clone();
nel metodo clone () ignorato e il lavoro è finito.
Deep Cloning :
Definizione: "A differenza della copia superficiale, una copia profonda è una copia completamente indipendente di un oggetto."
Significa quando un oggetto Employee contiene un altro oggetto personalizzato:
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
Quindi devi scrivere il codice per clonare anche l'oggetto "Indirizzo" nel metodo clone () ignorato. Altrimenti l'oggetto Indirizzo non clonerà e causerà un bug quando cambi il valore di Indirizzo nell'oggetto Dipendente clonato, che riflette anche quello originale.
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
Deep Copy
Una copia profonda copia tutti i campi e crea copie della memoria allocata dinamicamente indicata dai campi. Una copia profonda si verifica quando un oggetto viene copiato insieme agli oggetti a cui si riferisce.
Copia superficiale
La copia superficiale è una copia bit-saggia di un oggetto. Viene creato un nuovo oggetto che ha una copia esatta dei valori nell'oggetto originale. Se uno qualsiasi dei campi dell'oggetto è riferimento ad altri oggetti, vengono copiati solo gli indirizzi di riferimento, cioè viene copiato solo l'indirizzo di memoria.
Copia superficiale : la variabile di riferimento all'interno di oggetti originali e di copia superficiale fa riferimento all'oggetto comune .
Deep Copy - Le variabili di riferimento all'interno di oggetti originali e copiati in profondità hanno riferimenti a oggetti diversi .
clone esegue sempre una copia superficiale.
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();
}
}
la classe principale sta seguendo-
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
}
OutPut di cui sopra sarà-
falso vero vero
falso falso falso
Qualsiasi modifica apportata all'oggetto originale si rifletterà nell'oggetto superficiale non nell'oggetto profondo.
list.get(0).name="ViSuaLBaSiC";
System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName());
OutPut- ViSuaLBaSiC C
Vorrei fare un esempio piuttosto che una definizione formale.
var originalObject = {
a : 1,
b : 2,
c : 3,
};
Questo codice mostra una copia superficiale :
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
Questo codice mostra una copia profonda :
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
In termini semplici, una copia superficiale è simile a Chiama per riferimento e una copia profonda è simile a Chiama per valore
In Call By Reference, sia i parametri formali che quelli effettivi di una funzione si riferiscono alla stessa posizione di memoria e al valore.
In Call By Value, sia i parametri formali che quelli effettivi di una funzione si riferiscono a posizioni di memoria diverse ma con lo stesso valore.
Una copia superficiale costruisce un nuovo oggetto composto e inserisce i relativi riferimenti nell'oggetto originale.
A differenza della copia superficiale, la deepcopy costruisce un nuovo oggetto composto e inserisce anche copie degli oggetti originali dell'oggetto composto originale.
Facciamo un esempio.
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
Il codice sopra stampa FALSO.
Vediamo come.
Oggetto composto originale x=[1,[2]]
(chiamato come composto perché ha oggetto all'interno dell'oggetto (Inception))
come puoi vedere nell'immagine, c'è un elenco all'interno dell'elenco.
Quindi ne creiamo una copia superficiale usando y = copy.copy(x)
. Quello che Python fa qui è, creerà un nuovo oggetto composto ma gli oggetti al loro interno puntano verso gli oggetti originali.
Nell'immagine ha creato una nuova copia per l'elenco esterno. ma l'elenco interno rimane lo stesso di quello originale.
Ora ne creiamo una copia completa usando z = copy.deepcopy(x)
. ciò che python fa qui è, creerà un nuovo oggetto sia per la lista esterna che per quella interna. come mostrato nell'immagine seguente (rosso evidenziato).
Alla fine il codice viene stampato False
, poiché y e z non sono gli stessi oggetti.
HTH.
La copia superficiale sta creando un nuovo oggetto e quindi copiando i campi non statici dell'oggetto corrente nel nuovo oggetto. Se un campo è un tipo di valore -> viene eseguita una copia bit per bit del campo; per un tipo di riferimento -> il riferimento viene copiato ma l'oggetto indicato non lo è; pertanto l'oggetto originale e il suo clone si riferiscono allo stesso oggetto.
Deep copia sta creando un nuovo oggetto e quindi copiando i campi non statici dell'oggetto corrente nel nuovo oggetto. Se un campo è un tipo di valore -> viene eseguita una copia bit per bit del campo. Se un campo è un tipo di riferimento -> viene eseguita una nuova copia dell'oggetto riferito. Le classi da clonare devono essere contrassegnate come [Serializable].
Tratto da [blog]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
La copia profonda implica l'utilizzo del contenuto di un oggetto per creare un'altra istanza della stessa classe. In una copia profonda, i due oggetti possono contenere le stesse informazioni ma l'oggetto di destinazione avrà i propri buffer e risorse. la distruzione di entrambi gli oggetti non influirà sull'oggetto rimanente. L'operatore di assegnazione sovraccarico creerebbe una copia profonda degli oggetti.
La copia superficiale implica la copia del contenuto di un oggetto in un'altra istanza della stessa classe creando così un'immagine speculare. A causa della copia diretta di riferimenti e puntatori, i due oggetti condivideranno gli stessi contenuti contenuti esternamente dell'altro oggetto in modo imprevedibile.
Spiegazione:
Usando un costruttore di copie semplicemente copiamo i valori dei dati membro per membro. Questo metodo di copia si chiama copia superficiale. Se l'oggetto è una classe semplice, composta da tipi predefiniti e senza puntatori, ciò sarebbe accettabile. Questa funzione userebbe i valori e gli oggetti e il suo comportamento non verrebbe modificato con una copia superficiale, solo gli indirizzi dei puntatori che sono membri vengono copiati e non il valore a cui punta l'indirizzo. I valori dei dati dell'oggetto verrebbero quindi inavvertitamente modificati dalla funzione. Quando la funzione non rientra nell'ambito, la copia dell'oggetto con tutti i suoi dati viene espulsa dallo stack.
Se l'oggetto ha dei puntatori, è necessario eseguire una copia profonda. Con la copia profonda di un oggetto, la memoria viene allocata per l'oggetto nell'archivio libero e gli elementi puntati vengono copiati. Una copia profonda viene utilizzata per gli oggetti restituiti da una funzione.
Per aggiungere altro ad altre risposte,
La copia superficiale non creerà un nuovo riferimento, ma la copia profonda creerà il nuovo riferimento.
Ecco il programma per spiegare la copia profonda e superficiale.
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 + "]";
}
}
Copia di array:
L'array è una classe, il che significa che è un tipo di riferimento, quindi array1 = array2 risulta in due variabili che fanno riferimento allo stesso array.
Ma guarda questo esempio:
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]);
}
clone superficiale significa che viene copiata solo la memoria rappresentata dall'array clonato.
Se l'array contiene oggetti di tipo valore, i valori vengono copiati ;
se l'array contiene un tipo di riferimento, vengono copiati solo i riferimenti, quindi di conseguenza ci sono due array i cui membri fanno riferimento agli stessi oggetti .
Per creare una copia profonda, in cui i tipi di riferimento sono duplicati, è necessario eseguire il ciclo continuo dell'array e clonare manualmente ciascun elemento.
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]); }
Sono arrivato a capire dalle seguenti righe.
Copia superficiale copia i campi di un tipo di valore oggetto (int, float, bool) nell'oggetto target e i tipi di riferimento dell'oggetto (stringa, classe ecc.) Vengono copiati come riferimenti nell'oggetto target. In questo target, i tipi di riferimento indicheranno la posizione di memoria dell'oggetto sorgente.
Deep copy copia il valore di un oggetto e i tipi di riferimento in una nuova copia completa degli oggetti target. Ciò significa che sia ai tipi di valore che ai tipi di riferimento verrà assegnata una nuova posizione di memoria.
L'aggiunta a tutte le definizioni di cui sopra, un'altra copia profonda più comunemente usata, si trova nel costruttore della copia (o sovraccarico dell'assegnatore di funzioni) della classe.
Copia superficiale -> è quando non si fornisce il costruttore di copie. Qui viene copiato solo l'oggetto ma non tutti i membri della classe vengono copiati.
Deep copy -> è quando hai deciso di implementare il costruttore di copie o l'assegnazione del sovraccarico nella tua classe e ti permette di copiare tutti i membri della classe.
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
}
Il costruttore della copia viene utilizzato per inizializzare il nuovo oggetto con l'oggetto precedentemente creato della stessa classe. Di default il compilatore ha scritto una copia superficiale. La copia superficiale funziona correttamente quando non è coinvolta l'allocazione dinamica della memoria perché quando è coinvolta l'allocazione dinamica della memoria, entrambi gli oggetti punteranno verso la stessa posizione di memoria in un heap, pertanto per rimuovere questo problema abbiamo scritto una copia profonda in modo che entrambi gli oggetti abbiano una propria copia degli attributi in un ricordo. Per leggere i dettagli con esempi e spiegazioni complete, è possibile vedere i costruttori dell'articolo C ++ .