깊은 사본과 얕은 사본의 차이점은 무엇입니까?
깊은 사본과 얕은 사본의 차이점은 무엇입니까?
답변:
얕은 사본은 가능한 한 적게 복제합니다. 컬렉션의 얕은 복사본은 요소가 아니라 컬렉션 구조의 복사본입니다. 얕은 사본을 사용하면 이제 두 컬렉션이 개별 요소를 공유합니다.
딥 카피는 모든 것을 복제합니다. 컬렉션의 딥 카피는 원본 컬렉션의 모든 요소가 복제 된 두 컬렉션입니다.
폭 대 깊이; 객체를 루트 노드로 사용하는 참조 트리의 관점에서 생각하십시오.
얕은:
변수 A와 B는 서로 다른 메모리 영역을 나타내며, B가 A에 할당되면 두 변수는 동일한 메모리 영역을 나타냅니다. 나중에 내용 중 하나를 수정하면 내용을 공유 할 때 다른 내용에 즉시 반영됩니다.
깊은:
변수 A 및 B는 B가 A에 할당 될 때 A가 가리키는 메모리 영역의 값이 B가 가리키는 메모리 영역에 복사되는 메모리 영역의 값을 나타냅니다. 나중에 내용의 수정은 A 또는 B에 고유하게 유지됩니다. 내용은 공유되지 않습니다.
요컨대, 그것은 무엇을 가리키는 지에 달려 있습니다. 얕은 사본에서 객체 B는 메모리에서 객체 A의 위치를 가리 킵니다. 딥 카피에서는 객체 A의 메모리 위치에있는 모든 것이 객체 B의 메모리 위치에 복사됩니다.
이 위키 기사에는 훌륭한 다이어그램이 있습니다.
다음 이미지를 고려하십시오
예를 들어 Object.MemberwiseClone는 생성 얕은의 복사 링크를
특히 iOS 개발자의 경우 :
경우 B
A는 얕은 복사 의 A
그것처럼 원시 데이터를 다음 B = [A assign];
과 같이의 개체에 대한 B = [A retain]
;
B와 A는 동일한 메모리 위치를 가리 킵니다.
경우 B
A는 딥 카피 의가 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 기사를 참조하십시오.
얕은 사본 :
딥 카피 :
{C ++와 관련하여 동일한 유형 _t의 A와 B라는 두 개의 객체를 상상해보십시오.
얕은 사본 : 단순히 A에 대한 참조 사본을 B로 복사합니다. A의 주소 사본으로 생각하십시오. 따라서 A와 B의 주소는 동일합니다. 즉, 동일한 메모리 위치, 즉 데이터 내용을 가리 킵니다.
딥 카피 : A의 모든 멤버를 간단히 복사하고 B의 다른 위치에 메모리를 할당 한 다음 복사 된 멤버를 B에 할당하여 딥 카피를 달성합니다. 이런 식으로 A가 존재하지 않으면 B는 여전히 메모리에서 유효합니다. 올바른 용어는 클로닝인데, 둘 다 완전히 동일하지만 아직 다릅니다 (즉, 메모리 공간에 두 개의 다른 엔티티로 저장 됨). 포함 / 제외 목록을 통해 딥 카피 중에 선택할 속성을 결정할 수있는 복제 래퍼를 제공 할 수도 있습니다. 이것은 API를 만들 때 매우 일반적인 관행입니다.
관련된 스테이크를 이해하면 얕은 사본을 ONLY_IF로 선택할 수 있습니다 . C ++ 또는 C에서 처리 할 포인터가 너무 많으면 객체의 얕은 복사본을 수행하는 것이 실제로 나쁜 생각입니다.
EXAMPLE_OF_DEEP COPY_ 예를 들어, 이미지 처리 및 객체 인식을 시도 할 때 처리 영역에서 "무의미하고 반복적 인 모션"을 마스킹해야합니다. 이미지 포인터를 사용하는 경우 해당 마스크 이미지를 저장하도록 지정해야합니다. 지금 ... 이미지의 얕은 복사본을 수행하는 경우 포인터 참조가 스택에서 종료되면 참조와 해당 복사본이 손실됩니다. 즉, 어느 시점에서 액세스 위반의 런타임 오류가 발생합니다. 이 경우 필요한 것은 CLONING하여 이미지의 깊은 사본입니다. 이런 식으로 나중에 필요할 때 마스크를 검색 할 수 있습니다.
EXAMPLE_OF_SHALLOW_COPY StackOverflow 사용자와 비교할 때 지식이 충분 하지 않으므로이 부분을 삭제하고 명확하게 설명 할 수있는 좋은 예를 제시하십시오. 그러나 실제로 프로그램이 무한정 실행되는 것을 알고 있다면 함수 호출을 사용하여 스택에서 "푸시 팝"연속 작동하는 것을 알면 얕은 복사를하는 것이 좋지 않습니다. 아마추어 또는 초보자에게 무언가를 시연하는 경우 (예 : C / C ++ 튜토리얼). 그러나 감시 및 탐지 시스템 또는 Sonar Tracking System과 같은 응용 프로그램을 실행하는 경우 프로그램을 조만간 종료시킬 수 있으므로 대상을 얕게 복사하지 않아야합니다.
얕은 사본이란 무엇입니까?
얕은 사본은 객체의 비트 단위 사본입니다. 원본 객체의 값과 정확히 일치하는 새 객체가 생성됩니다. 객체의 필드 중 하나가 다른 객체에 대한 참조 인 경우 참조 주소 만 복사됩니다. 즉, 메모리 주소 만 복사됩니다.
이 그림에서이 MainObject1
필드가 field1
int 형, 그리고 ContainObject1
유형을 ContainObject
. 당신의 단순 복사본을 수행 할 때 MainObject1
, MainObject2
생성됩니다 field2
의 복사 된 값을 포함 field1
가리키는 여전히 및 ContainObject1
자체. 이후 있습니다 field1
원시적 형태의, 그 값이 복사됩니다 field2
하지만 이후는 ContainedObject1
, 목적은 MainObject2
여전히 가리 킵니다 ContainObject1
. 에 대한 변경 그래서 ContainObject1
에 MainObject1
반영됩니다 MainObject2
.
이제 이것이 얕은 사본이라면, 깊은 사본을 보자?
딥 카피 란 무엇입니까?
깊은 복사는 모든 필드를 복사하고 필드가 가리키는 동적으로 할당 된 메모리의 복사본을 만듭니다. 깊은 복사는 개체가 참조하는 개체와 함께 개체를 복사 할 때 발생합니다.
이 그림에서 MainObject1는 필드가 field1
int 형, 그리고 ContainObject1
유형을 ContainObject
. 당신의 깊은 사본을 수행 할 때 MainObject1
, MainObject2
생성됩니다 field2
의 복사 된 값을 포함 field1
하고 ContainObject2
의 복사 된 값을 포함 ContainObject1
. 에 대한 변경 주 ContainObject1
에 MainObject1
의 반영하지 것이다 MainObject2
.
field3
가 그 문제만큼 깊은 것을 시도하고 이해할 수있는 위치에있는 경우 를 언급하지만 그것은 당신의 잘못이 아닙니다.이 예제의 # 3은 어디에서 발생 ContainObject2
합니까?
객체 지향 프로그래밍에서 형식에는 멤버 필드 모음이 포함됩니다. 이러한 필드는 값 또는 참조 (즉, 값에 대한 포인터)로 저장 될 수 있습니다.
단순 사본에서는 유형의 새 인스턴스가 작성되고 값이 새 인스턴스로 복사됩니다. 참조 포인터도 값과 같이 복사됩니다. 따라서 참조는 원본 객체를 가리 킵니다. 참조로 저장된 멤버에 대한 변경 사항은 참조 된 오브젝트의 사본이 없으므로 원본과 사본 모두에 나타납니다.
딥 카피에서는 값으로 저장된 필드가 이전과 같이 복사되지만 참조로 저장된 객체에 대한 포인터는 복사되지 않습니다. 대신, 참조 된 객체로 딥 카피가 만들어지고 새 객체에 대한 포인터가 저장됩니다. 참조 된 객체를 변경해도 다른 객체 사본에는 영향을 미치지 않습니다.
얕은 복제 :
정의 : "개체의 얕은 사본은 '주된'물체를 복사하지만 내부 개체는 복사하지 않습니다." 사용자 정의 객체 (예 : Employee)에 기본, 문자열 유형 변수 만 있으면 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");
그런 다음 재정의 된 clone () 메서드뿐만 아니라 'Address'객체를 복제하는 코드를 작성해야합니다. 그렇지 않으면 Address 개체는 복제되지 않으며 복제 된 Employee 개체에서 Address 값을 변경할 때 버그가 발생하여 원래 개체도 반영합니다.
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
딥 카피
깊은 복사는 모든 필드를 복사하고 필드가 가리키는 동적으로 할당 된 메모리의 복사본을 만듭니다. 깊은 복사는 개체가 참조하는 개체와 함께 개체를 복사 할 때 발생합니다.
얕은 사본
얕은 사본은 객체의 비트 단위 사본입니다. 원본 객체의 값과 정확히 일치하는 새 객체가 생성됩니다. 객체의 필드 중 하나가 다른 객체에 대한 참조 인 경우 참조 주소 만 복사됩니다. 즉, 메모리 주소 만 복사됩니다.
얕은 복사 -원본 및 얕은 복사 된 개체 내의 참조 변수에는 공통 개체에 대한 참조가 있습니다.
딥 카피 -원본 및 딥 복사 된 객체 내부의 참조 변수에 다른 객체에 대한 참조가 있습니다 .
clone은 항상 얕은 복사를 수행합니다.
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은
거짓 사실
거짓 거짓 거짓
오리지널 오브젝트를 변경하면 깊은 오브젝트가 아닌 얕은 오브젝트에 반영됩니다.
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 !!
1 1 4 4 4 4 4 4
얕은 사본은 새 복합 객체를 구성하고 참조를 원본 객체에 삽입합니다.
얕은 복사와 달리 심도 복사는 새 복합 객체를 구성하고 원본 복합 객체의 원본 객체의 사본도 삽입합니다.
예를 들어 봅시다.
import copy
x =[1,[2]]
y=copy.copy(x)
z= copy.deepcopy(x)
print(y is z)
위의 코드는 FALSE를 인쇄합니다.
방법을 보자.
원래 화합물 오브젝트 x=[1,[2]]
(객체가 내부 객체 때문이라 화합물 (개시))
이미지에서 볼 수 있듯이 목록 안에 목록이 있습니다.
그런 다음을 사용하여 얕은 사본을 만듭니다 y = copy.copy(x)
. 파이썬이 여기서하는 일은 새로운 복합 객체를 만들지 만 내부의 객체는 orignal 객체를 가리키고 있습니다.
이미지에서 외부 목록에 대한 새 사본을 작성했습니다. 그러나 내부 목록은 원래 목록과 동일하게 유지됩니다.
이제 우리는를 사용하여 심층 복사를 만듭니다 z = copy.deepcopy(x)
. 파이썬이 여기서하는 일은 내부 목록뿐만 아니라 외부 목록에 대한 새 객체를 만듭니다. 아래 이미지에 표시된대로 (빨간색 강조 표시됨).
끝 코드 False
에서 y와 z는 동일한 객체가 아니기 때문에 prints .
HTH.
얕은 복사는 새 개체를 만든 다음 현재 개체의 고정되지 않은 필드를 새 개체로 복사합니다. 필드가 값 유형 인 경우 필드의 비트 별 복사가 수행됩니다. A에 대한 기준 입력 -> 참조 복사되지만 언급 객체 아니다; 따라서 원본 객체와 복제본은 동일한 객체를 참조합니다.
정밀 복사는 새 객체를 만든 다음 현재 객체의 비 정적 필드를 새 객체로 복사합니다. 필드가 값 유형 인 경우 필드의 비트 별 복사가 수행됩니다. 필드가 참조 유형 인 경우 참조 된 오브젝트의 새 사본이 수행됩니다. 복제 할 클래스는 [Serializable]로 플래그되어야합니다.
[블로그]에서 가져온 것 : http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html
딥 카피 에는 한 객체의 내용을 사용하여 동일한 클래스의 다른 인스턴스를 만듭니다. 딥 카피에서 두 객체에는 동일한 정보가 포함될 수 있지만 대상 객체에는 자체 버퍼와 리소스가 있습니다. 두 개체의 파괴는 나머지 개체에 영향을 미치지 않습니다. 오버로드 된 할당 연산자는 객체의 깊은 복사본을 만듭니다.
얕은 복사 는 한 객체의 내용을 같은 클래스의 다른 인스턴스에 복사하여 미러 이미지를 만드는 것을 포함합니다. 참조와 포인터를 직접 복사하기 때문에 두 객체는 예측할 수없는 다른 객체와 동일한 외부에 포함 된 내용을 공유합니다.
설명:
복사 생성자를 사용하여 멤버별로 데이터 값을 복사합니다. 이 복사 방법을 얕은 복사라고합니다. 객체가 내장 클래스로 구성되고 포인터가없는 간단한 클래스 인 경우 이는 허용됩니다. 이 함수는 값과 객체를 사용하며 그 동작은 얕은 복사로 변경되지 않으며 멤버 인 포인터의 주소 만 복사되며 주소가 가리키는 값은 아닙니다. 그러면 객체의 데이터 값이 함수에 의해 실수로 변경됩니다. 함수가 범위를 벗어나면 모든 데이터가 포함 된 객체의 복사본이 스택에서 튀어 나옵니다.
객체에 포인터가 있으면 깊은 복사를 실행해야합니다. 개체의 깊은 복사본을 사용하면 메모리가 여유 저장소의 개체에 할당되고 지정된 요소가 복사됩니다. 딥 카피는 함수에서 반환 된 객체에 사용됩니다.
얕은 사본은 새로운 참조를 생성하지 않지만 깊은 사본은 새로운 참조를 생성합니다.
깊고 얕은 사본을 설명하는 프로그램이 있습니다.
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 + "]";
}
}
일기 복사 :
Array는 클래스이므로 참조 유형이므로 array1 = array2는 동일한 배열을 참조하는 두 개의 변수가됩니다.
그러나이 예를보십시오 :
static void Main()
{
int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
int[] arr2 = new int[] { 6, 7, 8, 9, 0 };
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = arr1;
Console.WriteLine(arr1[2] + " " + arr2[2]);
arr2 = (int[])arr1.Clone();
arr1[2] = 12;
Console.WriteLine(arr1[2] + " " + arr2[2]);
}
얕은 복제 는 복제 된 배열이 나타내는 메모리 만 복사됨을 의미합니다.
배열에 값 유형 객체가 포함 된 경우 값이 복사됩니다 .
배열에 참조 유형이 포함 된 경우 참조 만 복사되므로 결과적으로 구성원이 동일한 객체를 참조하는 두 개의 배열이 있습니다 .
참조 유형이 중복되는 깊은 사본을 만들려면 배열을 반복하고 각 요소를 수동으로 복제해야합니다.
private void button1_Click(object sender, EventArgs e) { int[] arr1 = new int[]{1,2,3,4,5}; int[] arr2 = new int[]{6,7,8,9,0}; MessageBox.Show(arr1[2] + " " + arr2[2]); arr2 = arr1; MessageBox.Show(arr1[2] + " " + arr2[2]); arr1[2] = 12; MessageBox.Show(arr1[2] + " " + arr2[2]); }
나는 다음과 같은 내용을 이해하게되었습니다.
얕은 복사는 객체 값 유형 (int, float, bool) 필드를 대상 객체에 복사하고 객체의 참조 유형 (문자열, 클래스 등)은 대상 객체의 참조 로 복사됩니다 . 이 대상 참조 유형은 소스 객체의 메모리 위치를 가리 킵니다.
딥 카피는 객체의 값과 참조 유형을 대상 객체의 완전히 새로운 카피로 복사합니다. 이는 값 유형과 참조 유형 모두에 새 메모리 위치가 할당됨을 의미합니다.
위의 모든 정의에 가장 일반적으로 사용되는 하나 이상의 딥 카피는 클래스의 카피 생성자 (또는 오버로드 할당 연산자)에 있습니다.
얕은 복사->는 복사 생성자를 제공하지 않을 때입니다. 여기서는 개체 만 복사되지만 클래스의 모든 구성원은 복사되지 않습니다.
Deep copy->는 클래스에서 복사 생성자 또는 과부하 할당을 구현하기로 결정하고 클래스의 모든 멤버를 복사 할 수있게하는 시점입니다.
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
}