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

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

31 ответ

Решение

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

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

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

Мелкий:

Перед копированиемМелкое копированиеМелкий Готово

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

Deep:

Перед копированиемГлубокое копированиеГлубоко сделано

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

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

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

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

Одним словом, это зависит от того, что указывает на что. В мелкой копии объект 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

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

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

Пример:

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


Мелкая копия:

Мелкая копия


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

Deep Copy

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

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

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

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

Shallow Copy: просто делает копию ссылки на A в B. Думайте об этом как о копии адреса A. Таким образом, адреса A и B будут одинаковыми, т. Е. Они будут указывать на одну и ту же область памяти, то есть содержимое данных.

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

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

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

EXAMPLE_OF_SHALLOW_COPY Я не очень хорошо осведомлен по сравнению с пользователями в Stackru, поэтому не стесняйтесь удалять эту часть и приводите хороший пример, если можете уточнить. Но я действительно думаю, что не стоит делать поверхностное копирование, если вы знаете, что ваша программа будет работать в течение бесконечного периода времени, то есть непрерывной операции 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 а также ContainObject2 содержащий скопированное значение ContainObject1, Обратите внимание на любые изменения, внесенные в ContainObject1 в MainObject1 не будет отражаться в MainObject2,

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

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

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

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

Deep Copy

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

Мелкая копия

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

Пример глубокой и мелкой копии

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

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 !!

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

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

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");

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

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

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

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

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

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

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

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

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

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

Над кодом печатается ЛОЖЬ.

Давай посмотрим как.

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

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

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

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

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

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

НТН.

Представьте, что есть два массива с именами arr1 и 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);
}

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

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

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

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

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

Я пришел к пониманию из следующих строк.

Полное копирование копирует поля типа значения объекта (int, float, bool) в целевой объект, а ссылочные типы объекта (строка, класс и т. Д.) Копируются как ссылки в целевой объект. В этой цели ссылочные типы будут указывать на расположение памяти исходного объекта.

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

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

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

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

Объяснение:

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

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

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

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

Конструктор копирования используется для инициализации нового объекта ранее созданным объектом того же класса. По умолчанию компилятор пишет мелкую копию. Мелкое копирование работает хорошо, когда динамическое выделение памяти не задействовано, потому что, когда задействовано динамическое выделение памяти, оба объекта будут указывать на одну и ту же область памяти в куче. Поэтому, чтобы устранить эту проблему, мы написали глубокое копирование, чтобы оба объекта имели свою собственную копию атрибутов. в памяти. Для того, чтобы прочитать подробности с полными примерами и пояснениями, вы можете увидеть в статье конструкторы C++.

Чтобы добавить немного больше путаницы между мелкими копиями и просто назначить новое имя переменной для списка.

"Скажем, у нас есть:

x = [
    [1,2,3],
    [4,5,6],
    ]

Это утверждение создает 3 списка: 2 внутренних списка и один внешний список. Ссылка на внешний список становится доступной под именем x. Если мы делаем

y = x

данные не копируются. У нас все еще есть те же 3 списка в памяти где-то. Все это сделало доступным внешний список под именем y, в дополнение к его предыдущему имени x. Если мы делаем

y = list(x)

или же

y = x[:]

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам