오브젝트 복사
Object copying객체 지향 프로그래밍에서 객체 복사는 객체 지향 프로그래밍에서 데이터의 단위인 기존 객체의 복사본을 만드는 것입니다.결과 개체를 개체 복사본 또는 원래 개체의 복사본이라고 합니다.복사는 기본이지만 미묘한 문제가 있어 상당한 오버헤드가 발생할 수 있습니다.개체를 복사하는 방법은 여러 가지가 있는데, 가장 일반적인 방법은 복사 생성자 또는 복제입니다.복사는 대부분 복사를 수정 또는 이동하거나 현재 값을 보존할 수 있도록 이루어집니다.둘 중 하나가 필요하지 않은 경우 복사가 발생하지 않으므로 원본 데이터에 대한 참조가 충분하고 효율적입니다.
일반적으로 객체에는 복합 데이터가 저장됩니다.단순한 경우에서는 초기화되지 않은 새 개체를 할당하고 원래 개체에서 모든 필드(속성)를 복사하여 복사할 수 있지만 더 복잡한 경우에는 원하는 동작이 발생하지 않습니다.
복사 방법
대부분의 객체의 설계 목표는 단일 블록으로 만들어진 것과 유사하지만 대부분은 그렇지 않습니다.오브젝트가 여러 부분으로 구성되어 있기 때문에 복사는 중요하지 않습니다.이 문제를 해결하기 위한 몇 가지 전략이 있습니다.
필드i x를 포함하는 객체 A를 고려합니다(구체적으로는 A가 문자열이고i x가 해당 문자의 배열인지 고려합니다).A의 복사본을 만드는 데는 얕은 복사와 깊은 복사라는 다양한 전략이 있습니다.많은 언어에서 하나의 복사 작업 또는 개별 얕은 복사 [1]및 딥 복사 작업을 정의하여 하나 또는 둘 중 하나의 전략으로 범용 복사를 허용합니다.더 얕은 곳이라도 기존 객체 A에 대한 참조를 사용하는데, 이 경우 새로운 객체는 없고 새로운 참조만 사용됩니다.
얕은 복사와 깊은 복사라는 용어는 Smalltalk-80으로 [2]거슬러 올라간다.동일한 구별은 오브젝트를 동등하게 비교하는데 사용됩니다.기본적으로 동일성(같은 오브젝트)과 동등성(같은 값) 사이에는 차이가 있으며, 이는 얕은 동등성과 (1레벨) 두 오브젝트 참조의 깊은 동등성에 대응합니다.그러나 동등성이 문제의 오브젝트 필드만을 비교하는 것을 의미하는지 또는 부정하게 되는지를 나타냅니다.일부 또는 모든 필드를 ncing하고 해당 값을 차례로 비교합니다(예를 들어 노드가 같거나 값이 같으면 링크된 두 목록이 동일합니까?).[clarification needed]
얕은 복사
객체를 복사하는 한 가지 방법은 얕은 복사입니다.이 경우 새로운 오브젝트 B가 생성되고 A의 필드 값이 [3][4][5]B로 복사됩니다.이것은 필드별 복사,[6][7][8] 필드 간 복사 또는 필드 [9]복사라고도 합니다.필드 값이 객체(예를 들어 메모리 주소)에 대한 참조인 경우 참조를 복사하고, 따라서 A와 동일한 객체를 참조하며, 필드 값이 원시 유형인 경우 원시 유형의 값을 복사합니다.원시 유형(모든 것이 개체)이 없는 언어에서는 복사본 B의 모든 필드가 원본 A의 필드와 동일한 개체를 참조합니다.따라서 참조된 개체는 공유되기 때문에 이러한 개체 중 하나가 변경되면(A 또는 B에서) 다른 개체로 변경이 표시됩니다.얕은 복사본은 단순하고 일반적으로 비트를 정확하게 복사하는 것만으로 구현할 수 있기 때문에 일반적으로 비용이 저렴합니다.
상세 복사
다른 방법은 딥 복사본입니다. 즉, 필드가 참조되지 않습니다. 복사되는 개체에 대한 참조가 아니라 참조된 개체에 대해 새 복사 개체가 생성되고 B에 배치된 개체에 대한 참조가 생성됩니다.결과는 복사 B에서 참조하는 개체가 A에서 참조하는 개체와 구별되고 독립적이라는 점에서 얕은 복사가 제공하는 결과와 다릅니다.딥 복사본은 추가 객체를 생성해야 하므로 비용이 더 많이 들고 참조가 복잡한 그래프를 형성할 수 있으므로 훨씬 더 복잡할 수 있습니다.
상세 복사는 복사 프로세스가 반복적으로 발생하는 프로세스입니다.즉, 먼저 새 컬렉션 개체를 구성한 다음 원본에 있는 하위 개체의 복사본을 반복적으로 채우는 것을 의미합니다.딥 카피의 경우, 오브젝트의 카피는 다른 오브젝트에 카피됩니다.즉, 개체 복사본에 대한 변경 내용이 원래 개체에 반영되지 않습니다.python에서는 "deep copy()" 함수를 사용하여 구현됩니다.
조합
보다 복잡한 경우 복사본의 일부 필드에는 원래 개체와 공유된 값(천박한 복사본에서처럼)이 "관련성" 관계에 대응되어야 하며 일부 필드에는 "집약" 관계에 대응되는 복사본(딥 복사본에서처럼)이 있어야 합니다.이러한 경우 일반적으로 복사를 커스텀으로 구현해야 합니다.이 문제와 해결방법은 Smalltalk-80까지 거슬러 올라갑니다.[10]또는 필드를 얕은 복사 또는 딥 복사가 필요한 것으로 표시할 수 있습니다.또한 비교 조작과 마찬가지로 복사 [1]조작이 자동으로 생성됩니다.그러나 이것은 대부분의 객체 지향 언어에서는 구현되지 않지만 [1]에펠에서는 부분적으로 지원됩니다.
실행
거의 모든 객체 지향 프로그래밍 언어는 객체를 복사하는 방법을 제공합니다.대부분의 언어는 프로그램에 대부분의 객체를 제공하지 않기 때문에 프로그래머는 두 객체가 동일하거나 심지어 비교 가능한지 정의해야 하는 것과 마찬가지로 객체를 복사하는 방법을 정의해야 합니다.많은 언어가 몇 가지 기본 동작을 제공합니다.
복사가 어떻게 해결되는지는 언어마다 다르며, 복사가 가지고 있는 오브젝트의 개념도 다릅니다.
느릿느릿 복사
느린 복사본은 딥 복사본의 구현입니다.개체를 처음 복사할 때는 (빠른) 얕은 복사가 사용됩니다.카운터는 데이터를 공유하는 개체 수를 추적하는 데도 사용됩니다.프로그램이 오브젝트를 수정하고 싶을 때 (카운터를 검사하여) 데이터가 공유되는지 여부를 판단하고 필요에 따라 상세 복사를 수행할 수 있습니다.
레이지 카피는 겉보기에는 딥 카피로 보이지만, 가능한 한 얕은 카피의 속도를 활용합니다.단점은 다소 높지만 카운터 때문에 기본 비용이 일정하다는 것입니다.또한 특정 상황에서는 순환 참조가 문제를 일으킬 수 있습니다.
레이지 카피는 카피 온 라이트(Copy-on-write와 관련이 있습니다.
자바어
다음으로 가장 널리 사용되는 객체 지향 언어 중 하나인 Java의 예를 제시하겠습니다.Java는 객체 지향 언어가 이 문제를 처리할 수 있는 거의 모든 방법을 커버해야 합니다.
C++와 달리 Java의 오브젝트는 항상 참조를 통해 간접적으로 접근합니다.오브젝트는 암묵적으로 작성되지 않고 참조 변수에 의해 항상 전달 또는 할당됩니다.(Java의 메서드는 항상 값으로 전달됩니다만, 전달되는 것은 참조 변수의 값입니다).[11]Java Virtual Machine은 가비지 수집을 관리하여 개체에 더 이상 연결할 수 없는 후 개체가 정리되도록 합니다.Java에서는 지정된 개체를 자동으로 복사하는 방법은 없습니다.
복사는 보통 클래스의 clone() 메서드에 의해 수행됩니다.이 메서드는 보통 부모 클래스의 clone() 메서드를 호출하여 복사본을 얻은 후 임의의 커스텀복사 절차를 수행합니다.최종적으로 이것은 clone() 메서드에 도달합니다.Object
(최상위 클래스) 오브젝트와 같은 클래스의 새 인스턴스를 만들고 모든 필드를 새 인스턴스('최상위 클래스')에 복사합니다.이 메서드를 사용하는 경우 클래스는 다음 명령어를 구현해야 합니다.Cloneable
마커 인터페이스, 그렇지 않으면 Clone Not Supported가 느려집니다.예외.부모 클래스에서 복사본을 가져온 후 클래스 자체 clone() 메서드는 딥 복사(즉 객체에 의해 참조되는 구조의 일부 복제) 또는 새 인스턴스에 새로운 고유 ID를 부여하는 것과 같은 커스텀 복제 기능을 제공할 수 있습니다.
clone()의 반환 유형은 다음과 같습니다.Object
그러나 클론 메서드의 구현자는 Java가 공변 반환 유형을 지원하므로 클론되는 오브젝트의 유형을 대신 쓸 수 있습니다.clone()을 사용하면 덮어쓸 수 있는 메서드이기 때문에 임의의 오브젝트에서 clone()을 호출할 수 있습니다.클래스의 clone() 메서드를 사용할 수 있기 때문에 (복사 컨스트럭터에서는 clone()이 필요하게 됩니다.
단점은 추상형 clone() 메서드에 액세스할 수 없는 경우가 많다는 것입니다.Java의 대부분의 인터페이스 및 추상 클래스는 퍼블릭클론() 메서드를 지정하지 않습니다.따라서 대부분의 경우 clone() 메서드를 사용하는 유일한 방법은 객체의 클래스가 알려진 경우입니다.이는 가능한 한 일반적인 유형을 사용하는 추상화 원리에 반합니다.예를 들어 Java에 List 참조가 있는 경우 List는 퍼블릭클론() 메서드를 지정하지 않기 때문에 해당 참조에서 clone()을 호출할 수 없습니다.ArrayList나 LinkedList와 같은 List의 구현에는 일반적으로 clone() 메서드가 있지만 오브젝트의 클래스 타입을 가지고 다니는 것은 불편하고 잘못된 추상화입니다.
Java에서 개체를 복사하는 또 다른 방법은 를 통해 개체를 직렬화하는 것입니다.Serializable
인터페이스입니다.이는 일반적으로 지속성 및 와이어 프로토콜 목적으로 사용되지만 개체의 복사본을 생성하며, 복제와 달리 개체의 순환 그래프를 부드럽게 처리하는 딥 복사본을 프로그래머의 최소한의 노력으로 쉽게 사용할 수 있습니다.
두 방법 모두 클론 또는 시리얼라이제이션으로 복사된 오브젝트에는 컨스트럭터가 사용되지 않는다는 중요한 문제가 있습니다.이로 인해 데이터가 올바르게 초기화되지 않은 버그가 발생하여 멤버필드를 사용할 수 없게 되어 유지보수가 어려워질 수 있습니다.일부 유틸리티는 딥 클론 [12]라이브러리와 같은 딥 복사 개체에 대한 리플렉션을 사용하여 이러한 문제를 해결하려고 시도합니다.
에펠에서
Effel의 런타임 개체는 참조를 통해 간접적으로 액세스하거나 해당 개체를 사용하는 개체에 포함된 필드를 확장 개체로 액세스할 수 있습니다.즉, 객체의 필드는 외부 또는 내부에 저장됩니다.
에펠교실ANY
에는 개체의 얕은 복사 및 복제를 위한 기능이 포함되어 있습니다.모든 에펠 클래스는 에서 상속됩니다.ANY
따라서 이러한 기능은 모든 클래스에서 사용할 수 있으며 참조 개체와 확장 개체 모두에 적용할 수 있습니다.
그copy
이 기능은 오브젝트 간에 필드별로 얕은 복사를 수행합니다.이 경우 새 개체는 생성되지 않습니다.한다면y
에 복사되었다.x
에 의해 참조되는 동일한 오브젝트y
적용하기 전에copy
에 의해서도 참조됩니다.x
그 후copy
기능이 완료되었습니다.
의 얕은 복제인 새 오브젝트를 작성하려면y
, 기능twin
사용됩니다.이 경우 소스 필드와 동일한 필드를 사용하여 새 개체 하나가 생성됩니다.
기능twin
기능에 의존하다copy
의 하위 항목에서 재정의할 수 있습니다.ANY
(필요한 경우).의 결과twin
고정형입니다.like Current
.
기능을 사용하여 딥 복사 및 딥 트윈을 생성할 수 있습니다.deep_copy
그리고.deep_twin
, 다시 클래스에서 상속됩니다.ANY
. 이러한 기능은 전체 개체 구조에서 모든 개체를 복제하기 때문에 많은 새 개체를 만들 수 있습니다.기존 개체에 대한 참조를 단순히 복사하는 대신 새로운 중복 개체가 생성되므로 심층 작업이 얕은 작업보다 성능 문제의 원인이 되기 쉽습니다.
기타 언어
C#에서는 인터페이스를 사용하지 않고ICloneable
범용 확장 방법을 사용하여 리플렉션을 사용하여 딥 복사를 작성할 수 있습니다.여기에는 두 가지 장점이 있습니다.첫째, 수동으로 복사할 각 속성 및 변수를 지정하지 않고도 모든 개체를 복사할 수 있는 유연성을 제공합니다.둘째, 유형이 일반적이기 때문에 컴파일러는 대상 개체와 소스 개체가 동일한 유형을 가지도록 합니다.
Objective-C에서 방법copy
그리고.mutableCopy
는 모든 객체에 의해 상속되며 복사를 수행하기 위한 것입니다.복사는 원래 객체의 가변 유형을 작성하기 위한 것입니다.이러한 방법에서는, 다음의 이름을 붙입니다.copyWithZone
그리고.mutableCopyWithZone
복사를 수행하는 방법을 지정합니다.오브젝트는 대응하는 것을 실장해야 합니다.copyWithZone
복사 가능한 메서드입니다.
OCaml에서 라이브러리 함수 Oo.copy는 오브젝트를 얕은 복사한다.
Python에서 라이브러리의 복사 모듈은 오브젝트의 얕은 복사와 깊은 복사를 제공합니다.copy()
그리고.deepcopy()
각 기능을 수행합니다.[13]프로그래머는 특별한 메서드를 정의할 수 있습니다.__copy__()
그리고.__deepcopy__()
커스텀 카피 실장을 제공하는 오브젝트입니다.
루비에서는 모든 오브젝트가 얕은 복사를 수행하기 위한 두 가지 방법인 클론과 듀프를 상속합니다.두 가지 방법은 다르다clone
는 오브젝트의 오염된 상태, 프리즈 상태 및 싱글톤 메서드를 복사합니다.dup
오염된 상태만 복사합니다.개체의 바이트 스트림 또는 YAML 직렬화를 덤프 및 로드하여 딥 복사본을 만들 수 있습니다.[1] 또는 deep_dive gem을 사용하여 객체 그래프의 제어된 딥 복사를 수행할 수 있습니다.[2]
Perl에서, 중첩된 구조는 참조의 사용에 의해 저장됩니다. 따라서 개발자는 전체 구조 위에 루프하고 데이터를 다시 참조하거나 사용할 수 있습니다.dclone()
기능을 수행합니다.
VBA에서 유형의 변수 할당Object
는 얕은 복사입니다.다른 모든 유형(숫자 유형, 문자열, 사용자 정의 유형, 어레이)에 대한 할당은 깊은 복사입니다.그래서 키워드는Set
할당의 경우 얕은 복사와 (옵션) 키워드를 나타냅니다.Let
딥 카피를 나타냅니다.VBA에는 개체의 상세 복사본을 위한 기본 제공 방법이 없습니다.
「 」를 참조해 주세요.
메모들
- ^ a b c Grogono & Sakkinen 2000.
- ^ 골드버그 & 롭슨 1983, 97~99페이지."객체의 복사본을 만드는 방법은 두 가지가 있습니다.구별은 객체의 변수 값이 복사되는지 여부입니다.값이 복사되지 않으면 공유됩니다( ).
shallowCopy
값이 복사되면 공유되지 않습니다( ).deepCopy
)." - ^ "C++ Shallow vs Deep Copy Explanation".
- ^ ".NET Shallow vs Deep Copy Explanation".
- ^ "Generic Shallow vs Deep Copy Explanation". Archived from the original on 2016-03-04. Retrieved 2013-04-10.
- ^ 핵심 Java: 기초, 제1권, 295페이지
- ^ 유효 자바, 제2판, 페이지 54
- ^ "Object.clone()에 의해 수행된 이 필드별 복사본은 무엇입니까?", 스택 오버플로
- ^ "디자인에 관한 Josh Bloch: 효과적인 Java 저자 Josh Bloch와의 대화" (Bill Veners, Java World, 2002년 1월 4일, 페이지 13)
- ^ Goldberg & Robson 1983, 페이지 97. "기본 구현:
copy
이shallowCopy
복사로 인해 공유 변수와 비공유 변수의 특별한 조합이 발생할 필요가 있는 서브클래스에서는 일반적으로 복사와 관련된 메서드가 재실장됩니다.shallowCopy
또는deepCopy
." - ^ "Passing Information to a Method or a Constructor". Retrieved 8 October 2013.
- ^ Java 딥 클로닝 라이브러리
- ^ Python 복사 모듈
레퍼런스
- Goldberg, Adele; Robson, David (1983). Smalltalk-80: The Language and its Implementation. Palo Alto, California: Xerox Palo Alto Research Center. ISBN 978-0-201-11371-6.
- Grogono, Peter; Sakkinen, Markku (12 May 2000). "Copying and Comparing: Problems and Solutions" (PDF). In Elisa Bertino (ed.). Lecture Notes in Computer Science. ECOOP 2000 — Object-Oriented Programming. Vol. 1850. Springer Berlin Heidelberg. pp. 226–250. doi:10.1007/3-540-45102-1_11. Retrieved 2015-06-23.