Quelle est la différence entre une copie profonde et une copie superficielle?
Les copies superficielles dupliquent le moins possible. Une copie superficielle d'une collection est une copie de la structure de la collection, pas des éléments. Avec une copie superficielle, deux collections partagent désormais les éléments individuels.
Les copies profondes dupliquent tout. Une copie intégrale d'une collection est constituée de deux collections avec tous les éléments de la collection d'origine dupliqués.
Largeur vs profondeur; Pensez en termes d'arbre de références avec votre objet comme nœud racine.
Peu profond:
Les variables A et B font référence à différentes zones de la mémoire. Lorsque B est affecté à A, les deux variables font référence à la même zone de la mémoire. Les modifications ultérieures du contenu de l’un ou de l’autre se reflètent instantanément dans le contenu de l’autre, car elles partagent le même contenu.
Profond:
Les variables A et B se rapportent à différentes zones de la mémoire. Lorsque B est affecté à A, les valeurs de la zone de mémoire sur laquelle A pointe sont copiées dans la zone de mémoire sur laquelle B pointe. Les modifications ultérieures du contenu de l’un ou de l’autre restent propres à A ou à B; le contenu n'est pas partagé.
En bref, cela dépend de quoi pointe sur quoi. Dans une copie peu profonde, l'objet B pointe vers l'emplacement de l'objet A en mémoire. En copie profonde, tout ce qui se trouve dans l'emplacement mémoire de l'objet A est copié dans l'emplacement mémoire de l'objet B.
Cet article du wiki a un très bon diagramme.
Surtout pour les développeurs iOS:
Si B
est une copie superficielle de A
, alors pour les données primitives, il ressemble à B = [A assign];
et pour les objets, il ressemble à B = [A retain]
;
B et A pointent vers le même emplacement mémoire
Si B
est une copie profonde de A
, c'est comme B = [A copy];
B et A pointent vers différents emplacements de mémoire
L'adresse mémoire B est identique à celle de A
B a le même contenu que A
Copie superficielle: copie les valeurs de membre d'un objet dans un autre.
Copie profonde: copie les valeurs de membre d'un objet dans un autre.
Tous les objets de pointeur sont dupliqués et copiés à fond.
Exemple:
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.)
Je n'ai pas vu de réponse courte et facile à comprendre ici - je vais donc l'essayer.
Avec une copie superficielle, tout objet pointé par la source est également pointé par la destination (afin qu'aucun objet référencé ne soit copié).
Avec une copie profonde, tout objet pointé par la source est copié et la copie est pointée par la destination (il y aura donc 2 de chaque objet référencé). Cela revient dans l'arborescence des objets.
Pour faciliter la compréhension, vous pouvez suivre cet article: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
Copie superficielle:
Copie profonde:
{Imaginez deux objets: A et B du même type _t (en ce qui concerne C++) et vous songez à une copie superficielle/profonde de A à B}
Copie peu profonde: .__ Fait simplement une copie de la référence de A dans B. Pensez-y comme une copie de l'adresse de A . Ainsi, les adresses de A et de B seront les mêmes pointez sur le même emplacement mémoire, c’est-à-dire le contenu des données.
Copie profonde: .__ Effectue simplement une copie de tous les membres de A, alloue de la mémoire à un emplacement différent pour B, puis assigne les membres copiés à B pour obtenir une copie en profondeur. De cette façon, si A devient inexistant, B est toujours valide dans la mémoire. Le terme correct à utiliser serait le clonage, où vous savez que les deux sont totalement identiques, mais néanmoins différents (c'est-à-dire stockés sous deux entités différentes dans l'espace mémoire). Vous pouvez également indiquer à votre wrapper de clonage où vous pouvez choisir, via une liste d'inclusion/exclusion, les propriétés à sélectionner lors de la copie en profondeur. C'est une pratique assez courante lorsque vous créez des API.
Vous pouvez choisir de faire une copie superficielle ONLY_IF vous comprenez les enjeux. Lorsque vous avez un nombre considérable de pointeurs à traiter en C++ ou C, faire une copie superficielle d'un objet estVRAIMENTune mauvaise idée.
EXAMPLE_OF_DEEP COPY_ Par exemple, lorsque vous essayez de traiter et de reconnaître des objets, vous devez masquer les "mouvements non pertinents et répétitifs" de vos zones de traitement. Si vous utilisez des pointeurs d'image, vous avez peut-être les spécifications pour enregistrer ces images de masque. NOW ... si vous faites une copie superficielle de l'image, lorsque les références de pointeur sont supprimées de la pile, vous perdez la référence et sa copie, c.-à-d. Qu'il y aura une erreur d'exécution de violation d'accès à un moment donné. Dans ce cas, vous avez besoin d’une copie en profondeur de votre image en la clonant. De cette façon, vous pouvez récupérer les masques au cas où vous en auriez besoin ultérieurement.
EXAMPLE_OF_SHALLOW_COPY Je ne suis pas extrêmement compétent par rapport aux utilisateurs de StackOverflow, alors n'hésitez pas à supprimer cette partie et à mettre un bon exemple si vous pouvez clarifier. Mais je pense vraiment que ce n’est pas une bonne idée de faire une copie superficielle si vous savez que votre programme va fonctionner pendant une période infinie, c’est-à-dire une opération continue "Push-pop" sur la pile avec des appels de fonction. Si vous démontrez quelque chose à un amateur ou à un novice (par exemple, un didacticiel C/C++), c'est probablement correct. Mais si vous exécutez une application telle qu'un système de surveillance et de détection, ou Sonar Tracking System, vous n'êtes pas censé garder une copie superficielle de vos objets car cela tuerait votre programme tôt ou tard.
char * Source = "Hello, world.";
char * ShallowCopy = Source;
char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);
'ShallowCopy' pointe vers le même emplacement en mémoire, tandis que 'Source' le fait . 'DeepCopy' pointe vers un emplacement différent en mémoire, mais le contenu est identique.
Qu'est-ce que la copie superficielle?
Une copie superficielle est une copie au format bit d'un objet. Un nouvel objet est créé avec une copie exacte des valeurs de l'objet d'origine. Si l'un des champs de l'objet fait référence à d'autres objets, seules les adresses de référence sont copiées, c'est-à-dire que seule l'adresse de la mémoire est copiée.
Dans cette figure, le MainObject1
contient les champs field1
de type int et ContainObject1
de type ContainObject
. Lorsque vous effectuez une copie superficielle de MainObject1
, MainObject2
est créé avec field2
contenant la valeur copiée de field1
et pointant toujours vers ContainObject1
lui-même. Notez que, puisque field1
est de type primitif, sa valeur est copiée dans field2
mais puisque ContainedObject1
est un objet, MainObject2
pointe toujours sur ContainObject1
. Ainsi, toute modification apportée à ContainObject1
dans MainObject1
sera reflétée dans MainObject2
.
Maintenant, s’il s’agit d’une copie superficielle, voyons quelle est la copie en profondeur?
Qu'est-ce que Deep Copy?
Une copie en profondeur copie tous les champs et crée des copies de la mémoire allouée dynamiquement et pointée par les champs. Une copie en profondeur se produit lorsqu'un objet est copié avec les objets auxquels il fait référence.
Dans cette figure, MainObject1 contient les champs field1
de type int et ContainObject1
de type ContainObject
. Lorsque vous effectuez une copie complète de MainObject1
, MainObject2
est créé avec field2
contenant la valeur copiée de field1
et ContainObject2
contenant la valeur copiée de ContainObject1
. Notez que les modifications apportées à ContainObject1
dans MainObject1
ne seront pas reflétées dans MainObject2
.
En programmation orientée objet, un type comprend une collection de champs membres. Ces champs peuvent être stockés soit par valeur, soit par référence (c'est-à-dire un pointeur sur une valeur).
Dans une copie superficielle, une nouvelle instance du type est créée et les valeurs sont copiées dans la nouvelle instance. Les pointeurs de référence sont également copiés, tout comme les valeurs. Par conséquent, les références pointent vers les objets d'origine. Toute modification apportée aux membres stockés par référence apparaît à la fois dans l'original et dans la copie, car aucune copie de l'objet référencé n'a été réalisée.
Dans une copie profonde, les champs stockés par valeur sont copiés comme auparavant, mais les pointeurs sur les objets stockés par référence ne sont pas copiés. Au lieu de cela, une copie complète de l'objet référencé est créée et un pointeur sur le nouvel objet est stocké. Toute modification apportée à ces objets référencés n'affectera pas les autres copies de l'objet.
"ShallowCopy" pointe vers le même emplacement en mémoire que "Source". 'DeepCopy' pointe vers un emplacement différent en mémoire, mais le contenu est le même.
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
Copie peu profonde - La variable de référence à l'intérieur des objets originaux et ceux dont la copie est peu profonde font référence à common object.
Copie profonde - Les variables de référence à l'intérieur des objets originaux et copiés à fond font référence à différent objet.
le clone fait toujours une copie superficielle.
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 suit-
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 de ci-dessus sera-
faux vrai vrai
faux faux faux
Toute modification apportée à l'objet d'origine sera reflétée dans un objet peu profond et non dans un objet profond.
list.get(0).name="ViSuaLBaSiC";
System.out.println(shallow.get(0).getName()+" "+deep.get(0).getName());
OutPut- ViSuaLBaSiC C
Clonage superficiel:
Définition: "Une copie superficielle d’un objet copie l’objet‘ principal ’, mais ne copie pas les objets internes." Lorsqu'un objet personnalisé (par exemple, Employee) a juste des variables primitives, de type String, vous utilisez alors le clonage peu profond.
Employee e = new Employee(2, "john cena");
Employee e2=e.clone();
Vous renvoyez super.clone();
dans la méthode clone () remplacée et votre travail est terminé.
Deep Cloning :
Définition: "Contrairement à la copie superficielle, une copie complète est une copie totalement indépendante d'un objet."
Signifie lorsqu'un objet Employé contient un autre objet personnalisé:
Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");
Ensuite, vous devez écrire le code pour cloner l'objet 'Address' également dans la méthode clone () remplacée. Sinon, l'objet Address ne clone pas et provoque un bogue lorsque vous modifiez la valeur de Address dans l'objet Employee cloné, qui reflète également l'original.
Copie en profondeur
Une copie en profondeur copie tous les champs et crée des copies de la mémoire allouée dynamiquement et pointée par les champs. Une copie en profondeur se produit lorsqu'un objet est copié avec les objets auxquels il fait référence.
Copie superficielle
Une copie superficielle est une copie au format bit d'un objet. Un nouvel objet est créé avec une copie exacte des valeurs de l'objet d'origine. Si l'un des champs de l'objet fait référence à d'autres objets, seules les adresses de référence sont copiées, c'est-à-dire que seule l'adresse mémoire est copiée.
Je voudrais donner l'exemple plutôt que la définition formelle.
var originalObject = {
a : 1,
b : 2,
c : 3,
};
Ce code montre une copie peu profonde:
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
Ce code montre une copie deep:
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 !!
En termes simples, une copie superficielle est similaire à Call By Reference et une copie complète est similaire à Call By Value.
Dans Appel par référence, les paramètres formels et réels d'une fonction font référence au même emplacement mémoire et à la même valeur.
Dans Appel par valeur, les paramètres formels et réels d’une fonction font référence à un emplacement de mémoire différent mais ayant la même valeur.
Imaginez qu'il y ait deux tableaux appelés arr1 et arr2.
arr1 = arr2; //shallow copy
arr1 = arr2.clone(); //deep copy
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);
}
Une copie superficielle construit un nouvel objet composé et y insère ses références à l'objet d'origine.
Contrairement à la copie superficielle, Deepcopy construit un nouvel objet composé et insère également des copies des objets d'origine de l'objet composé d'origine.
Prenons un exemple.
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
Le code ci-dessus affiche FAUX.
Voyons comment.
Objet composé d'origine x=[1,[2]]
(appelé en tant que composé car il contient un objet à l'intérieur de l'objet (Inception))
comme vous pouvez le voir dans l'image, il y a une liste dans la liste.
Ensuite, nous en créons une copie superficielle en utilisant y = copy.copy(x)
. Ce que python fait ici, il créera un nouvel objet composé mais les objets qu’ils contiennent pointent vers les objets d’origine.
Dans l'image, il a créé une nouvelle copie pour la liste externe. mais la liste intérieure reste la même que celle d'origine.
Nous en créons maintenant une copie profonde en utilisant z = copy.deepcopy(x)
. ce que python fait ici, il créera un nouvel objet pour la liste externe ainsi que pour la liste interne. comme indiqué dans l'image ci-dessous (surbrillance rouge).
À la fin, le code affiche False
, car y et z ne sont pas les mêmes objets.
HTH.
Pour ajouter plus à d'autres réponses,
Tiré de [blog]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
Deep Copy implique l'utilisation du contenu d'un objet pour créer une autre instance de la même classe. Dans une copie profonde, les deux objets peuvent contenir les mêmes informations, mais l'objet cible aura ses propres tampons et ressources. la destruction de l'un ou l'autre objet n'affectera pas l'objet restant. L'opérateur d'affectation surchargé créerait une copie complète des objets.
Copie superficielle implique la copie du contenu d'un objet dans une autre instance de la même classe, créant ainsi une image miroir. En raison de la copie directe des références et des pointeurs, les deux objets partageront le même contenu contenu en externe de l'autre objet pour être imprévisible.
Explication:
À l'aide d'un constructeur de copie, nous copions simplement les valeurs de données membre par membre. Cette méthode de copie est appelée copie superficielle. Si l'objet est une classe simple, composée de types intégrés et d'aucun pointeur, cela serait acceptable. Cette fonction utiliserait les valeurs et les objets et son comportement ne serait pas modifié avec une copie superficielle. Seules les adresses des pointeurs membres sont copiées et non la valeur vers laquelle l'adresse pointe. Les valeurs de données de l'objet seraient alors modifiées par inadvertance par la fonction. Lorsque la fonction sort de la portée, la copie de l'objet avec toutes ses données est extraite de la pile.
Si l'objet a des pointeurs, une copie en profondeur doit être exécutée. Avec la copie profonde d'un objet, la mémoire est allouée pour l'objet dans le magasin libre et les éléments pointés sont copiés. Une copie en profondeur est utilisée pour les objets renvoyés par une fonction.
Une copie superficielle ne créera pas de nouvelle référence, mais une copie intégrale créera la nouvelle référence.
Voici le programme pour expliquer la copie profonde et peu profonde.
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 + "]";
}
}
Shallow copie crée un nouvel objet, puis copie les champs non statiques de l'objet actuel dans le nouvel objet. Si un champ est un type de valeur -> une copie bit par bit du champ est effectuée; pour un type reference -> la référence est copiée mais l'objet référencé ne l'est pas; Par conséquent, l'objet d'origine et son clone font référence au même objet.
Deep copy crée un nouvel objet, puis copie les champs non statiques de l'objet actuel dans le nouvel objet. Si un champ est un type valeur ->, une copie bit par bit du champ est effectuée. Si un champ est un type référence ->, une nouvelle copie de l'objet référencé est effectuée. Les classes à cloner doivent être marquées comme [Serializable].
Copier des ararys:
Array est une classe, ce qui signifie qu'il s'agit d'un type de référence, donc array1 = array2 resultsin dans deux variables référençant le même tableau.
Mais regardez cet exemple:
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]);
}
shallow clone signifie que seule la mémoire représentée par le tableau cloné est copiée.
Si le tableau contient des objets de type valeur, les valeurs sont copiées ;
Si le tableau contient un type de référence, seules les références sont copiées. Par conséquent, il y a deux tableaux dont les membres font référence aux mêmes objets .
Pour créer une copie détaillée - lorsque les types de référence sont dupliqués, vous devez parcourir le tableau et cloner chaque élément manuellement.
Le constructeur de copie est utilisé pour initialiser le nouvel objet avec l'objet précédemment créé de la même classe. Par défaut, le compilateur a écrit une copie superficielle. La copie superficielle fonctionne bien lorsque l’allocation de mémoire dynamique n’est pas impliquée, car les deux objets pointent vers le même emplacement mémoire dans un segment. Par conséquent, pour éliminer ce problème, nous avons écrit une copie profonde afin que les deux objets possèdent leur propre copie dans une mémoire . Pour lire les détails avec des exemples complets et des explications, voir l'article Constructeurs C++ .
Pour ajouter juste un peu plus de confusion entre copie superficielle et simplement assigner un nouveau nom de variable à lister.
"Disons que nous avons:
x = [
[1,2,3],
[4,5,6],
]
Cette instruction crée 3 listes: 2 listes internes et une liste externe. Une référence à la liste externe est alors rendue disponible sous le nom x. Si nous faisons
y = x
aucune donnée n'est copiée. Nous avons toujours les mêmes 3 listes en mémoire quelque part. Cela n'a fait que rendre la liste externe disponible sous le nom y, en plus de son nom précédent x. Si nous faisons
y = list(x)
ou
y = x[:]
Cela crée une nouvelle liste avec le même contenu que x. La liste x contenait une référence aux 2 listes internes, de sorte que la nouvelle liste contiendrait également une référence à ces 2 mêmes listes internes. Une seule liste est copiée - la liste externe . Il y a maintenant 4 listes en mémoire, les deux listes internes, la liste externe et la copie de la liste externe. La liste externe d'origine est disponible sous le nom x et la nouvelle liste externe est disponible sous le nom y.
Les listes internes n'ont pas été copiées! Vous pouvez accéder et modifier les listes internes à partir de x ou de y à ce stade!
Si vous avez une liste à deux dimensions (ou supérieure), ou un type quelconque de structure de données imbriquée, et que vous souhaitez effectuer une copie complète de tout, vous souhaitez utiliser la fonction deepcopy () dans le module de copie. Votre solution fonctionne également pour les listes 2D, car elle effectue une itération sur les éléments de la liste externe et en crée une copie, puis crée une nouvelle liste externe pour toutes les copies internes. "
Je suis venu à comprendre des lignes suivantes.
Une copie superficielle copie un objet type de valeur (int, float, bool) dans l'objet cible et les types de référence de l'objet (chaîne, classe, etc.) sont copiés en tant que references dans l'objet cible. Dans cette cible, les types de référence pointeront vers l'emplacement mémoire de l'objet source.
Deep Copy copie les types valeur et référence d'un objet dans une nouvelle copie complète des objets cible. Cela signifie que les types de valeur et les types de référence se verront attribuer un nouvel emplacement mémoire.
En plus de toutes les définitions ci-dessus, une copie complète la plus couramment utilisée se trouve dans le constructeur de la copie (ou l'opérateur de l'affectation de surcharge) de la classe.
Une copie superficielle -> est lorsque vous ne fournissez pas le constructeur de copie. Ici, seul l'objet est copié mais tous les membres de la classe ne sont pas copiés.
Deep copy -> est le moment où vous avez décidé d'implémenter le constructeur de copie ou l'affectation de surcharge dans votre classe et qui permet de copier tous les membres de la 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
}