В чем разница между глубокой копией и поверхностной копией?

StackOverflow https://stackoverflow.com/questions/184710

Вопрос

В чем разница между глубокой копией и поверхностной копией?

Это было полезно?

Решение

Мелкие копии дублируют как можно меньше. Мелкая копия коллекции - это копия структуры коллекции, а не ее элементы. С мелкой копией две коллекции теперь совместно используют отдельные элементы.

Глубокие копии дублируют все. Глубокая копия коллекции - это две коллекции со всеми дублированными элементами оригинальной коллекции.

Другие советы

Ширина против глубины;думайте о дереве ссылок, в котором ваш объект является корневым узлом.

Мелкий:

Before Copy Shallow Copying Shallow Done

Переменные A и B относятся к разным областям памяти, когда B присваивается A, две переменные относятся к одной и той же области памяти.Более поздние изменения содержимого одного из них мгновенно отражаются в содержимом другого, поскольку они имеют общее содержимое.

Глубокий:

Before Copy Deep Copying Deep Done

Переменные A и B относятся к различным областям памяти, когда B присваивается A, значения в области памяти, на которую указывает A, копируются в область памяти, на которую указывает B.Более поздние модификации содержимого любого из них остаются уникальными для A или B;содержимое не является общим.

Короче говоря, это зависит от того, что указывает на что. В мелкой копии объект B указывает на местоположение объекта A в памяти. При глубоком копировании все объекты в ячейке памяти объекта A копируются в ячейку памяти объекта B.

В этой вики-статье есть отличная схема.

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 указывают на разные области памяти

адрес памяти B такой же, как у адреса A

B содержит то же содержимое, что и A

Попробуйте рассмотреть следующее изображение

 введите описание изображения здесь

Например, Object.MemberwiseClone создает мелкую копию ссылка

и используя интерфейс ICloneable , вы можете получить копию deep , как описано здесь

Мелкая копия:Копирует значения членов из одного объекта в другой.

Глубокая копия:Копирует значения членов из одного объекта в другой.
Любые объекты-указатели дублируются и глубоко копируются.

Пример:

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.)

Я не видел короткого, легкого для понимания ответа здесь - поэтому я попробую.

При неглубоком копировании любой объект, на который указывает источник, также указывается адресатом (так что ссылочные объекты не копируются).

При глубокой копии любой объект, на который указывает источник, копируется, а на копию указывает пункт назначения (так что теперь будет 2 на каждый объект, на который имеется ссылка). Это повторяется по дереву объектов.

{Представьте себе два объекта: A и B одного типа _t (по отношению к C ++), и вы думаете о мелком / глубоком копировании A в B}

Мелкая копия . Просто сделайте копию ссылки на A в B. Думайте об этом как о копии адреса A. Таким образом, адреса A и B будут одинаковыми, то есть они будут указывать на одну и ту же ячейку памяти, то есть содержимое данных.

Глубокая копия: Просто делает копию всех членов A, выделяет память в другом месте для B, а затем назначает скопированные элементы B для получения глубокого копирования. Таким образом, если A становится несуществующим, B все еще действует в памяти. Правильный термин для использования будет клонировать, когда вы знаете, что они оба абсолютно одинаковы, но все же разные (т.е. хранятся как две разные сущности в пространстве памяти). Вы также можете предоставить свою обертку для клонов, где вы можете решить, используя список включения / исключения, какие свойства выбрать при глубоком копировании. Это довольно распространенная практика, когда вы создаете API.

Вы можете сделать мелкую копию ONLY_IF , в которой понимаете ставки. Когда у вас есть огромное количество указателей для работы в C ++ или C, создание мелкой копии объекта является ДЕЙСТВИТЕЛЬНО плохой идеей.

EXAMPLE_OF_DEEP COPY _ Например, когда вы пытаетесь выполнить обработку изображений и распознавание объектов, вам необходимо скрыть " Неактуальное и повторяющееся движение " из ваших областей обработки. Если вы используете указатели изображений, то у вас может быть спецификация для сохранения этих изображений маски. СЕЙЧАС ... если вы делаете поверхностную копию изображения, когда ссылки указателя УБИРАЮТСЯ из стека, вы потеряете ссылку и ее копию, то есть в какой-то момент произойдет ошибка нарушения доступа. В этом случае вам понадобится глубокая копия вашего изображения путем клонирования. Таким образом, вы можете получить маски на случай, если они понадобятся вам в будущем.

EXAMPLE_OF_SHALLOW_COPY Я не очень хорошо осведомлен по сравнению с пользователями в StackOverflow, поэтому не стесняйтесь удалять эту часть и приводите хороший пример, если можете уточнить. Но я действительно думаю, что не стоит делать поверхностное копирование, если вы знаете, что ваша программа будет работать в течение бесконечного периода времени, то есть непрерывного «push-pop». работа над стеком с вызовами функций. Если вы демонстрируете что-то любителю или новичку (например, учебные материалы по C / C ++), то это, вероятно, хорошо. Но если вы используете приложение, такое как система наблюдения и обнаружения, или Sonar Tracking System, вы не должны продолжать поверхностное копирование ваших объектов, потому что это рано или поздно убьет вашу программу.

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 .

Теперь, если это мелкая копия, давайте посмотрим, что такое глубокая копия?

Что такое Deep Copy?

Глубокая копия копирует все поля и создает копии динамически распределенной памяти, на которую указывают поля. Глубокое копирование происходит, когда объект копируется вместе с объектами, на которые он ссылается. Deep Copy

На этом рисунке MainObject1 имеет поля field1 типа int и ContainObject1 типа ContainObject . Когда вы делаете глубокую копию MainObject1 , MainObject2 создается с field2 , содержащим скопированные значения field1 и < code> ContainObject2 , содержащий скопированное значение ContainObject1 . Обратите внимание, что любые изменения, внесенные в ContainObject1 в MainObject1 , не будут отражены в MainObject2 .

хорошая статья

В объектно-ориентированном программировании тип включает в себя коллекцию полей-членов. Эти поля могут храниться либо по значению, либо по ссылке (т. Е. Указатель на значение).

В неглубокой копии создается новый экземпляр типа, а значения копируются в новый экземпляр. Ссылочные указатели также копируются как значения. Поэтому ссылки указывают на исходные объекты. Любые изменения в элементах, которые хранятся по ссылке, отображаются как в оригинале, так и в копии, поскольку не было сделано ни одной копии ссылочного объекта.

В глубокой копии поля, которые хранятся по значению, копируются, как и раньше, но указатели на объекты, сохраненные по ссылке, не копируются. Вместо этого делается глубокая копия ссылочного объекта, и указатель на новый объект сохраняется. Любые изменения, внесенные в эти объекты, на которые есть ссылки, не влияют на другие копии объекта.

«ShallowCopy» указывает на то же место в памяти, что и «Source». 'DeepCopy' указывает на другое место в памяти, но его содержимое одинаково.

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

Мелкое клонирование:
Определение: «Мелкая копия объекта копирует & # 8216; main & # 8217; объект, но не копирует внутренние объекты. " Когда пользовательский объект (например, Employee) имеет только примитивные переменные типа String, тогда вы используете Shallow Cloning.

Employee e = new Employee(2, "john cena");
Employee e2=e.clone();

Вы возвращаете super.clone (); в переопределенном методе clone (), и ваша работа завершена.

Глубокое клонирование :
Определение: " В отличие от мелкой копии, глубокая копия является полностью независимой копией объекта. & Quot;
Означает, что объект Employee содержит другой пользовательский объект:

Employee e = new Employee(2, "john cena", new Address(12, "West Newbury", "Massachusetts");

Затем вы должны написать код для клонирования объекта 'Address' также в переопределенном методе clone (). В противном случае объект Address не будет клонирован, и это приведет к ошибке при изменении значения Address в клонированном объекте Employee, который также отражает исходный объект.

Глубокая копия

Глубокая копия копирует все поля и создает копии динамически распределенной памяти, на которую указывают поля. Глубокое копирование происходит, когда объект копируется вместе с объектами, на которые он ссылается.

Мелкая копия

Мелкая копия - побитовая копия объекта. Создается новый объект, который имеет точную копию значений в исходном объекте. Если какие-либо поля объекта являются ссылками на другие объекты, копируются только ссылочные адреса, т. Е. Копируется только адрес памяти.

Мелкое копирование . Ссылочная переменная внутри оригинальных и мелко скопированных объектов имеет ссылку на общий объект.

Глубокая копия . Ссылочная переменная внутри оригинальных и глубоко скопированных объектов имеет ссылку на другой объект.

  

клон всегда копирует мелко.

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

} 

OutPut выше будет -

  

false true true

     

ложь ложь ложь

Любые изменения, сделанные в оригинальном объекте, будут отражаться на мелком объекте, а не на глубоком объекте.

  list.get(0).name="ViSuaLBaSiC";
  System.out.println(shallow.get(0).getName()+"  "+deep.get(0).getName());

OutPut - ViSuaLBaSiC C

Я хотел бы привести пример, а не формальное определение.

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 !!
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);
}

В простых терминах поверхностная копия похожа на вызов по ссылке, а глубокая копия похожа на вызов по значению

В вызове по ссылке и формальные, и фактические параметры функции ссылаются на одну и ту же ячейку памяти и значение.

В вызове по значению формальные и фактические параметры функции относятся к разным ячейкам памяти, но имеют одинаковое значение.

Представьте, что есть два массива с именами arr1 и arr2.

arr1 = arr2;   //shallow copy
arr1 = arr2.clone(); //deep copy

Мелкое копирование создает новый объект, а затем копирует нестатические поля текущего объекта в новый объект. Если поле является типом значения - > выполняется побитовая копия поля; для ссылочного типа - > ссылка копируется, а указанный объект - нет; поэтому исходный объект и его клон ссылаются на один и тот же объект.

Глубокая копия создает новый объект, а затем копирует нестатические поля текущего объекта в новый объект. Если поле имеет тип значения - > побитовая копия поля выполняется. Если поле имеет ссылочный тип - > выполняется новая копия указанного объекта. Клонируемые классы должны быть помечены как [Сериализуемые].

Взято из [блога]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Глубокая копия предполагает использование содержимого одного объекта для создания другого экземпляра того же класса.В глубокой копии два объекта могут содержать одну и ту же информацию, но целевой объект будет иметь свои собственные буферы и ресурсы.уничтожение любого объекта не повлияет на оставшийся объект.Перегруженный оператор присваивания создаст глубокую копию объектов.

Мелкая копия включает копирование содержимого одного объекта в другой экземпляр того же класса, создавая таким образом зеркальное отображение.Благодаря прямому копированию ссылок и указателей два объекта будут использовать одно и то же внешнее содержимое другого объекта, что будет непредсказуемо.

Объяснение:

Используя конструктор копирования, мы просто копируем значения данных элемент за элементом.Этот метод копирования называется поверхностным копированием.Если объект представляет собой простой класс, состоящий из встроенных типов и без указателей, это будет приемлемо.Эта функция будет использовать значения и объекты, и ее поведение не будет изменено при мелкой копии, копируются только адреса указателей, которые являются членами, а не значение, на которое указывает адрес.Тогда значения данных объекта будут непреднамеренно изменены функцией.Когда функция выходит за пределы области видимости, копия объекта со всеми его данными извлекается из стека.

Если у объекта есть указатели, необходимо выполнить глубокую копию.При глубокой копии объекта память выделяется для объекта в свободном хранилище, и элементы, на которые указывают, копируются.Глубокая копия используется для объектов, возвращаемых функцией.

Чтобы добавить больше к другим ответам,

  • Неупомянутая копия объекта выполняет копию по значению для свойств на основе типов значений и копий по ссылке для справочных типов.
  • Глубокая копия объекта выполняет копию по значению для свойств, основанных на типах значений, а также копировать по значению для справочных свойств, основанных на иерархии (ссылочных типов)

Неглубокая копия создает новый составной объект и вставляет в него ссылки на исходный объект.

В отличие от поверхностного копирования, глубокое копирование создает новый составной объект, а также вставляет копии исходных объектов исходного составного объекта.

Давайте возьмем пример.

import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)

Код выше печатает FALSE.

Давайте посмотрим, как это сделать.

Исходный составной объект x=[1,[2]] (называемый составным, потому что у него есть объект внутри объекта (Начало))

enter image description here

как вы можете видеть на изображении, внутри списка есть список.

Затем мы создаем его неглубокую копию, используя y = copy.copy(x).Здесь Python создает новый составной объект, но объекты внутри него указывают на исходные объекты.

enter image description here

На изображении создана новая копия внешнего списка.но внутренний список остается таким же, как исходный.

Теперь мы создаем его глубокую копию, используя z = copy.deepcopy(x).Что здесь делает Python, так это то, что он создает новый объект как для внешнего списка, так и для внутреннего списка.как показано на изображении ниже (выделено красным).

enter image description here

В конце печатается код False, поскольку y и z не являются одними и теми же объектами.

ХТХ.

Мелкая копия не создаст новую ссылку, но глубокая копия создаст новую ссылку.

Вот программа для объяснения глубокой и мелкой копии.

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 + "]";
    }
}

Копирование статей:

Массив - это класс, что означает, что это ссылочный тип, поэтому результаты 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]);
    }

мелкий клон означает, что копируется только память, представленная клонированным массивом.

Если массив содержит объекты типа значения, значения копируются ;

если массив содержит ссылочный тип, копируются только ссылки, поэтому в результате получается два массива, члены которых ссылаются на одни и те же объекты .

Чтобы создать глубокую копию & # 8212 ;, где ссылочный тип дублируется, вы должны пройти через массив и клонировать каждый элемент вручную.

Добавление ко всем вышеприведенным определениям еще одной и наиболее часто используемой глубокой копии происходит в конструкторе копирования (или в перегрузочном операторе присваивания) класса.

Мелкая копия - > это когда вы не предоставляете конструктор копирования. Здесь копируется только объект, но копируются не все члены класса.

Глубокая копия - > когда вы решили реализовать конструктор копирования или перегрузку в своем классе и разрешить копирование всех членов класса.

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)

или

y = x[:]

Это создаст новый список с тем же содержимым, что и x.Список x содержал ссылку на два внутренних списка, поэтому новый список также будет содержать ссылку на те же самые два внутренних списка.Копируется только один список — внешний.Теперь в памяти есть 4 списка: два внутренних списка, внешний список и копия внешнего списка.Исходный внешний список доступен под именем x, а новый внешний список доступен под именем y.

Внутренние списки не копировались!На этом этапе вы можете получить доступ к внутренним спискам и редактировать их из x или y!

Если у вас есть двумерный (или более высокий) список или любая вложенная структура данных, и вы хотите сделать полную копию всего, тогда вам нужно использовать функцию deepcopy() в модуле копирования.Ваше решение также работает для двумерных списков, поскольку перебирает элементы внешнего списка и создает копию каждого из них, а затем создает новый внешний список для всех внутренних копий».

источник: https://www.reddit.com/r/learnpython/comments/1afldr/why_is_copying_a_list_so_damn_difficult_in_python/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top