อะไรคือความแตกต่างระหว่างสำเนาลึกกับสำเนาตื้น


754

อะไรคือความแตกต่างระหว่างสำเนาลึกกับสำเนาตื้น


6
เทคโนโลยีใดที่อยู่ภายใต้
Suresh Varma

42
@SureshVarma เป็นแนวคิดการเขียนโปรแกรม!
Manish Shrivastava

คำตอบ:


760

สำเนาตื้นซ้ำให้น้อยที่สุด สำเนาตื้นของคอลเลกชันเป็นสำเนาของโครงสร้างการเก็บรวบรวมไม่ใช่องค์ประกอบ ด้วยสำเนาตื้นตอนนี้ทั้งสองคอลเลกชันแบ่งปันองค์ประกอบแต่ละ

สำเนาลึกทำซ้ำทุกอย่าง สำเนาที่ลึกของคอลเลกชันคือสองคอลเลกชันที่มีองค์ประกอบทั้งหมดในคอลเลกชันดั้งเดิมที่ซ้ำกัน


อาจเป็น. NET MemberwiseClone () การใช้งานทำได้มากกว่าการทำสำเนาแบบตื้นในความหมายทั่วไป
Lu55

5
โปรดจำไว้ว่ายังมีสำเนาผสม (ไม่เพียง แต่เช่นขี้เกียจคัดลอก ) ซึ่งทำซ้ำส่วนหนึ่งของมัน ( นี่เป็นตัวอย่าง )! ;)
cregox

ดังนั้นสำเนาตื้น ๆ ของ X สามารถเปลี่ยนองค์ประกอบใน X แต่สำเนาลึกไม่สามารถ?
punstress

1
โครงสร้างการรวบรวมคืออะไร?
ฮันนี่

1
@Honey Collections สามารถเป็นโครงสร้างข้อมูลที่หลากหลายซึ่งจัดเก็บข้อมูลหลายรายการ ในไพ ธ อนเรามีสิ่งอันดับ, รายการ, พจนานุกรมและอื่น ๆ
Murphy

850

ความกว้างและความลึก; คิดในแง่ของต้นไม้อ้างอิงกับวัตถุของคุณเป็นรูทโหนด

ตื้น:

ก่อนที่จะคัดลอก การคัดลอกตื้น เสร็จแล้ว

ตัวแปร A และ B อ้างถึงพื้นที่ต่าง ๆ ของหน่วยความจำเมื่อ B ถูกกำหนดให้กับ A ตัวแปรทั้งสองอ้างถึงพื้นที่หน่วยความจำเดียวกัน การปรับเปลี่ยนเนื้อหาของทั้งสองอย่างต่อมาจะปรากฏในเนื้อหาของคนอื่นทันทีในขณะที่แบ่งปันเนื้อหา

ลึก:

ก่อนที่จะคัดลอก คัดลอกลึก เสร็จสิ้นแล้ว

ตัวแปร A และ B อ้างถึงพื้นที่ต่าง ๆ ของหน่วยความจำเมื่อ B ถูกกำหนดให้กับค่าในพื้นที่หน่วยความจำซึ่งจุด A ที่จะถูกคัดลอกไปยังพื้นที่หน่วยความจำที่จุด B การดัดแปลงเนื้อหาของภายหลังนั้นยังคงไม่ซ้ำกับ A หรือ B เนื้อหาจะไม่ถูกแชร์


32
นี่คือบทความวิกิพีเดียที่ภาพประกอบนี้มาจากในกรณีที่มันไม่ได้ทำให้ความรู้สึกออกไปจากบริบทสำหรับคุณen.wikipedia.org/wiki/Object_copy#Shallow_copy
คอร์

4
ในกรณีของการคัดลอกตื้น ๆ หากเราทำการเปลี่ยนแปลงใด ๆ ในอาเรย์ B ที่จะแสดงในอาเรย์ A เนื่องจาก A & B ทั้งสองชี้ไปยังตำแหน่งหน่วยความจำเดียวกัน
tek3

3
ในบรรทัดเดียวการคัดลอกโดยอ้างอิงกับการคัดลอกตามค่า ไม่แน่ใจว่าคำตอบนั้นถูกต้องหรือไม่!
Mannu

2
ภาพที่ถ่ายโดยตรงจากวิกิพีเดียโดยไม่มีการอ้างอิง
jasonleonhard

9
@jasonleonhard ดังนั้น 9 ปีที่แล้วฉันเพิ่งนำ URL มาใส่ในรูปภาพเพราะไม่สนับสนุนการฝังรูปภาพ ดังนั้น URL จึงอ้างอิงแหล่งที่มา ชุมชนในภายหลังทำให้ URL เป็นภาพที่ฝังโดยไม่ต้องแก้ไขการอ้างอิงบางอย่าง ความคิดเห็นยอดนิยมอายุ 4 ปียังชี้ให้เห็นสิ่งที่คุณชี้ให้เห็น ลองดูที่: stackoverflow.com/posts/184780/revisionsทำไมไม่เพียงแค่แก้ไขการอ้างอิงเป็นคำตอบด้วยตัวเอง? ฉันอาจไม่สามารถใช้งานได้ในครั้งต่อไปที่มีคนร้องเรียนเกี่ยวกับรูปแบบการเขียนอายุ 10 ปีของฉัน
dlamblin

156

ในระยะสั้นมันขึ้นอยู่กับสิ่งที่ชี้ไปที่อะไร ในสำเนาตื้นวัตถุ B ชี้ไปยังตำแหน่งของวัตถุ A ในหน่วยความจำ ในสำเนาลึกทุกสิ่งในตำแหน่งหน่วยความจำของวัตถุ A จะถูกคัดลอกไปยังตำแหน่งหน่วยความจำของวัตถุ B

บทความ wiki นี้มีไดอะแกรมที่ยอดเยี่ยม

http://en.wikipedia.org/wiki/Object_copy


113

ลองพิจารณาภาพต่อไปนี้

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่างเช่นObject.MemberwiseCloneสร้างลิงค์คัดลอกตื้น

และใช้อินเทอร์เฟซICloneableคุณสามารถรับสำเนาลึกตามที่อธิบายไว้ที่นี่


28
ภาพที่มีค่าพันคำ.
Levi Fuller

6
โอ้เด็กชายมาที่นี่เพื่อหาความหมาย นี่เป็นคำตอบเดียวที่ช่วยได้
Karan Singh

1
นี่เป็นวิธีที่ง่ายที่สุดและยังแสดงให้เห็นถึงสิ่งที่จำเป็นเท่านั้น
hina10531

1
ภาพประกอบที่ดีที่สุด
มูฮัมหมัด

69

โดยเฉพาะอย่างยิ่งสำหรับนักพัฒนา iOS:

หากBเป็นสำเนาตื้นของAแล้วสำหรับข้อมูลดั้งเดิมที่มันต้องการB = [A assign];และสำหรับวัตถุที่มันต้องการB = [A retain];

B และ A ชี้ไปที่ตำแหน่งหน่วยความจำเดียวกัน

หากBเป็นสำเนาลึกของAมันก็เป็นเช่นนั้นB = [A copy];

B และ A ชี้ไปยังตำแหน่งหน่วยความจำที่แตกต่างกัน

ที่อยู่หน่วยความจำ B เหมือนกับ A

B มีเนื้อหาเช่นเดียวกับของ


8
"ที่อยู่หน่วยความจำ B เหมือนกับของ A" - เป็นอย่างไรบ้าง

2
ใน Deep Copy "ที่อยู่หน่วยความจำ B จะไม่เหมือนกับ A's"
ismail baig

60

คัดลอกตื้น: คัดลอกค่าสมาชิกจากวัตถุหนึ่งไปอีกวัตถุหนึ่ง

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

47

ฉันไม่ได้เห็นคำตอบสั้น ๆ และเข้าใจง่ายที่นี่ - ดังนั้นฉันจะลองดู

ด้วยการคัดลอกตื้นวัตถุใด ๆ ที่ชี้ไปยังต้นทางจะถูกชี้ไปยังปลายทางด้วย (เพื่อไม่ให้คัดลอกวัตถุที่อ้างอิง)

ด้วยการทำสำเนาแบบลึกวัตถุใด ๆ ที่ชี้ไปยังแหล่งที่มาจะถูกคัดลอกและสำเนานั้นจะถูกชี้ไปยังปลายทาง (ดังนั้นตอนนี้จะมี 2 ของแต่ละวัตถุที่อ้างอิง) สิ่งนี้จะซ้ำต้นไม้วัตถุ



36

{ลองนึกภาพวัตถุสองชิ้น: A และ B ที่มีประเภทเดียวกัน _t (เทียบกับ C ++) และคุณกำลังคิดเกี่ยวกับการคัดลอก A / B ที่ตื้น / ลึก}

คัดลอกตื้น: เพียงแค่ทำสำเนาของการอ้างอิงถึง A เป็น B. คิดว่ามันเป็นสำเนาที่อยู่ของ ดังนั้นที่อยู่ของ A และ B จะเหมือนกันนั่นคือที่อยู่เหล่านั้นจะถูกชี้ไปยังตำแหน่งหน่วยความจำเดียวกันนั่นคือเนื้อหาข้อมูล

Deep copy: เพียงทำสำเนาสมาชิกทั้งหมดของ A จัดสรรหน่วยความจำในตำแหน่งอื่นสำหรับ B แล้วมอบหมายสมาชิกที่คัดลอกไปที่ B เพื่อให้ได้สำเนาลึก ด้วยวิธีนี้หาก A กลายเป็น B ที่ไม่มีอยู่จริงยังคงใช้ได้ในหน่วยความจำ คำที่ถูกต้องที่จะใช้คือการโคลนนิ่งซึ่งคุณรู้ว่าพวกเขาทั้งคู่เหมือนกัน แต่แตกต่างกัน (เช่นเก็บไว้เป็นเอนทิตีที่แตกต่างกันสองอันในพื้นที่หน่วยความจำ) นอกจากนี้คุณยังสามารถให้ wrapper โคลนของคุณที่คุณสามารถตัดสินใจผ่านรายการรวม / การยกเว้นคุณสมบัติที่จะเลือกในระหว่างการคัดลอกลึก นี่เป็นวิธีปฏิบัติทั่วไปเมื่อคุณสร้าง API

คุณสามารถเลือกที่จะทำสำเนาตื้นONLY_IF ที่คุณเข้าใจสเตคที่เกี่ยวข้อง เมื่อคุณมีพอยน์เตอร์จำนวนมากที่ต้องจัดการกับใน C ++ หรือ C การทำสำเนาวัตถุตื้น ๆ นั้นเป็นความคิดที่ผิดจริงๆ

EXAMPLE_OF_DEEP COPY_ตัวอย่างคือเมื่อคุณพยายามทำการประมวลผลภาพและการรับรู้วัตถุคุณต้องปิดบัง "การเคลื่อนไหวที่ไม่เกี่ยวข้องและซ้ำซ้อน" ออกจากพื้นที่การประมวลผลของคุณ หากคุณใช้พอยน์เตอร์รูปภาพคุณอาจมีข้อกำหนดในการบันทึกอิมเมจหน้ากากเหล่านั้น ตอนนี้ ... ถ้าคุณทำสำเนาตื้นของภาพเมื่อตัวชี้อ้างอิงถูกฆ่าจากสแต็กคุณสูญเสียการอ้างอิงและสำเนาของมันนั่นคือจะมีข้อผิดพลาดรันไทม์ของการละเมิดการเข้าถึงในบางจุด ในกรณีนี้สิ่งที่คุณต้องการคือสำเนาที่มีความลึกของภาพโดยการปิดมัน ด้วยวิธีนี้คุณสามารถดึงมาสก์ได้ในกรณีที่คุณต้องการในอนาคต

EXAMPLE_OF_SHALLOW_COPYฉันไม่ได้มีความรู้มากเมื่อเทียบกับผู้ใช้ใน StackOverflow ดังนั้นโปรดลบส่วนนี้และวางตัวอย่างที่ดีถ้าคุณสามารถชี้แจง แต่ฉันคิดว่ามันเป็นความคิดที่ดีที่จะทำสำเนาตื้น ๆ ถ้าคุณรู้ว่าโปรแกรมของคุณจะทำงานในช่วงเวลาที่ไม่มีที่สิ้นสุดนั่นคือการดำเนินการ "push-pop" อย่างต่อเนื่องเหนือสแต็กด้วยการเรียกใช้ฟังก์ชัน หากคุณแสดงให้เห็นถึงบางสิ่งบางอย่างแก่ผู้สมัครเล่นหรือมือใหม่ (เช่นเนื้อหาการสอน C / C ++) ก็อาจไม่เป็นไร แต่ถ้าคุณใช้งานแอพพลิเคชั่นเช่นระบบตรวจจับและตรวจจับหรือระบบติดตามโซนาร์คุณไม่ควรทำการคัดลอกวัตถุที่อยู่รอบ ๆ เพราะมันจะฆ่าโปรแกรมของคุณไม่ช้าก็เร็ว


32
char * Source = "Hello, world.";

char * ShallowCopy = Source;    

char * DeepCopy = new char(strlen(Source)+1);
strcpy(DeepCopy,Source);        

'ShallowCopy' ชี้ไปที่ตำแหน่งเดียวกันในหน่วยความจำเช่นเดียวกับ 'แหล่งที่มา' 'DeepCopy' ชี้ไปยังตำแหน่งอื่นในหน่วยความจำ แต่เนื้อหาเหมือนกัน


22

Shallow Copy คืออะไร

การคัดลอกตื้นเป็นสำเนาที่ชาญฉลาดของวัตถุ มีการสร้างวัตถุใหม่ที่มีสำเนาที่แน่นอนของค่าในวัตถุต้นฉบับ หากฟิลด์ใด ๆ ของวัตถุเป็นการอ้างอิงไปยังวัตถุอื่นจะคัดลอกเฉพาะที่อยู่อ้างอิงเท่านั้นเท่านั้นจะถูกคัดลอกที่อยู่หน่วยความจำคัดลอกตื้น

ในรูปนี้MainObject1มีเขตข้อมูลfield1ชนิด int และชนิดContainObject1 ContainObjectเมื่อคุณทำสำเนาตื้นMainObject1, MainObject2ถูกสร้างขึ้นด้วยfield2ที่มีมูลค่าการคัดลอกfield1และยังคงชี้ไปที่ContainObject1ตัวเอง หมายเหตุว่าตั้งแต่field1เป็นชนิดดั้งเดิมค่าของมันจะถูกคัดลอกไปfield2แต่เนื่องจากContainedObject1เป็นวัตถุยังคงชี้ไปที่MainObject2 ContainObject1ดังนั้นการเปลี่ยนแปลงใด ๆ ที่ทำContainObject1ในจะปรากฏในMainObject1MainObject2

ทีนี้ถ้านี่คือสำเนาตื้น ๆ ลองมาดูกันว่าสำเนาลึกคืออะไร?

Deep Copy คืออะไร

สำเนาลึกจะคัดลอกฟิลด์ทั้งหมดและทำสำเนาหน่วยความจำที่จัดสรรแบบไดนามิกที่ชี้ไปตามฟิลด์ การทำสำเนาแบบลึกจะเกิดขึ้นเมื่อวัตถุถูกคัดลอกพร้อมกับวัตถุที่มันอ้างถึง คัดลอกลึก

ในรูปนี้ MainObject1 มีเขตข้อมูลfield1ชนิด int และชนิดContainObject1 ContainObjectเมื่อคุณทำสำเนาลึกMainObject1, MainObject2ถูกสร้างขึ้นด้วยfield2ที่มีมูลค่าการคัดลอกfield1และมีความคุ้มค่าของการคัดลอกContainObject2 ContainObject1โปรดทราบว่าการเปลี่ยนแปลงใด ๆ ที่ทำContainObject1ในMainObject1จะไม่ส่งผลMainObject2ต่อ

บทความที่ดี


มันไม่ใช่ความผิดของคุณแม้ว่าตัวอย่างนี้หมายถึงสิ่งfield3ที่เมื่ออยู่ในฐานะที่จะลองและเข้าใจบางสิ่งบางอย่างที่ลึกกว่าปัญหานั้น # 3 ในตัวอย่างนั้นเกิดขึ้นContainObject2 ที่ไหน
Robb_2015

16

ในการเขียนโปรแกรมเชิงวัตถุชนิดรวมถึงชุดของเขตข้อมูลสมาชิก ฟิลด์เหล่านี้อาจถูกจัดเก็บตามค่าหรือตามการอ้างอิง (เช่นตัวชี้ไปยังค่า)

ในสำเนาแบบตื้นจะมีการสร้างอินสแตนซ์ใหม่ของประเภทและค่าจะถูกคัดลอกลงในอินสแตนซ์ใหม่ ตัวชี้การอ้างอิงจะถูกคัดลอกเช่นเดียวกับค่า ดังนั้นการอ้างอิงจึงชี้ไปยังวัตถุต้นฉบับ การเปลี่ยนแปลงใด ๆ กับสมาชิกที่เก็บไว้โดยการอ้างอิงจะปรากฏทั้งในต้นฉบับและสำเนาเนื่องจากไม่มีการทำสำเนาของวัตถุที่อ้างอิง

ในสำเนาลึกเขตข้อมูลที่เก็บโดยค่าจะถูกคัดลอกเหมือน แต่ตัวชี้ไปยังวัตถุที่เก็บไว้โดยอ้างอิงจะไม่ถูกคัดลอก แต่จะทำสำเนาลึกของวัตถุที่อ้างอิงและตัวชี้ไปยังวัตถุใหม่จะถูกเก็บไว้ การเปลี่ยนแปลงใด ๆ ที่ทำกับวัตถุที่อ้างอิงจะไม่มีผลกับสำเนาอื่น ๆ ของวัตถุ


12

'ShallowCopy' ชี้ไปที่ตำแหน่งเดียวกันในหน่วยความจำเช่นเดียวกับ 'แหล่งที่มา' 'DeepCopy' ชี้ไปยังตำแหน่งอื่นในหน่วยความจำ แต่เนื้อหาเหมือนกัน


นี่เป็นสิ่งที่ทำให้เข้าใจผิดเล็กน้อย ทั้งสำเนาตื้นและลึกจะคัดลอกวัตถุไปยังตำแหน่งใหม่ในหน่วยความจำลึกก็จะคัดลอกวัตถุเด็กในขณะที่ตื้นจะเพิ่งมีวัตถุใหม่หมายถึงเด็กเก่า อ่านยากโดยไม่อ้างอิงถึงวัตถุดั้งเดิม
Bill K

10

การ
เลียนแบบตื้น:คำจำกัดความ: "สำเนาตื้นของวัตถุคัดลอกวัตถุ 'หลัก' แต่ไม่คัดลอกวัตถุภายใน เมื่อวัตถุที่กำหนดเอง (เช่นพนักงาน) มีเพียงดั้งเดิมตัวแปรชนิดสตริงจากนั้นคุณใช้การโคลนแบบตื้น

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

คุณกลับมาsuper.clone();ที่เมธอด clone () ที่แทนที่และงานของคุณจบแล้ว

การโคลนลึก :
คำจำกัดความ: "ไม่เหมือนสำเนาตื้นสำเนาลึกเป็นสำเนาอิสระของวัตถุ"
หมายถึงเมื่อวัตถุพนักงานเก็บวัตถุที่กำหนดเองอื่น:

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

จากนั้นคุณต้องเขียนรหัสเพื่อคัดลอกวัตถุ 'ที่อยู่' และในวิธีการแทนที่ () มิฉะนั้นวัตถุที่อยู่จะไม่ลอกแบบและจะทำให้เกิดข้อผิดพลาดเมื่อคุณเปลี่ยนค่าของที่อยู่ในการคัดลอกวัตถุของพนักงานซึ่งสะท้อนให้เห็นถึงวัตถุเดิมเช่นกัน


8
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

นั่นไม่ใช่ตัวอย่างที่ดี ส่วนใหญ่จะใช้สำหรับการคัดลอกตื้นคัดลอกวัตถุโดยไม่ต้องคัดลอกข้อมูล แต่เมื่อวัตถุต้องการแก้ไขข้อมูลที่ใช้ร่วมกันสำเนาลึกของมันจะถูกนำมาใช้ ตัวอย่างของคุณอาจทำให้ผู้เริ่มต้นสับสน
CMircea

ใช้ได้เฉพาะในภาษาที่ใช้พอยน์เตอร์เพื่อแสดงสตริง จุดที่ DHA พยายามทำคือการคัดลอกพอยน์เตอร์ซ้ำกับพอยน์เตอร์ที่เหมือนกัน (เอกพจน์) เนื้อหาต้นฉบับในขณะที่การคัดลอกแบบลึกจะทำการโคลนนิ่งเนื้อหาที่อ้างอิงของพอยน์เตอร์เช่นกัน ทั้งสองวิธีคัดลอกเนื้อหาพื้นผิว หากภาษาเก็บสตริงเป็นเนื้อหาตัวอักษรเช่นในส่วนหัวของ WAV ตัวอย่างนี้จะไม่ทำงาน โปรดทราบว่านี่อาจเป็นเรื่องพิถีพิถันเกินไปสำหรับปัญหาในชีวิตจริงส่วนใหญ่ที่ไม่ใช่เรื่องลึกลับ
DragonLord

8

คัดลอกลึก

สำเนาลึกจะคัดลอกฟิลด์ทั้งหมดและทำสำเนาหน่วยความจำที่จัดสรรแบบไดนามิกที่ชี้ไปตามฟิลด์ การทำสำเนาแบบลึกจะเกิดขึ้นเมื่อวัตถุถูกคัดลอกพร้อมกับวัตถุที่มันอ้างถึง

คัดลอกตื้น

การคัดลอกตื้นเป็นสำเนาที่ชาญฉลาดของวัตถุ มีการสร้างวัตถุใหม่ที่มีสำเนาที่แน่นอนของค่าในวัตถุต้นฉบับ หากฟิลด์ใด ๆ ของวัตถุเป็นการอ้างอิงไปยังวัตถุอื่นเพียงคัดลอกที่อยู่อ้างอิงเท่านั้นเช่นคัดลอกที่อยู่หน่วยความจำเท่านั้น


ลิงค์นั้นน่าเศร้าที่ใช้ไม่ได้อีกต่อไป - ตอนนี้ชี้ไปที่บทความตั้งแต่เดือนกุมภาพันธ์ 2019 เกี่ยวกับการออกแบบเว็บ
PhilPhil

7

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


7

ฉันต้องการยกตัวอย่างมากกว่าคำจำกัดความที่เป็นทางการ

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
Suresh Prajapati

ในสำเนาลึกทำ copyObject.a = 8 แล้วตรวจสอบ หวังว่าคุณจะได้คำตอบที่เหมาะสม
Vivek Mehta

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

5

ในเงื่อนไขอย่างง่ายสำเนาตื้นจะคล้ายกับ Call By Reference และสำเนาลึกคล้ายกับ Call By Value

ใน Call By Reference พารามิเตอร์ทั้งแบบเป็นทางการและตามจริงของฟังก์ชั่นหมายถึงตำแหน่งหน่วยความจำเดียวกันและค่า

ใน Call By Value ทั้งพารามิเตอร์ที่เป็นทางการและที่เกิดขึ้นจริงของฟังก์ชั่นหมายถึงตำแหน่งหน่วยความจำที่แตกต่างกัน แต่มีค่าเดียวกัน


5

ลองนึกภาพมีสองอาร์เรย์ที่เรียกว่า arr1 และ arr2

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

5

สำเนาตื้นสร้างวัตถุผสมใหม่และแทรกการอ้างอิงลงในวัตถุต้นฉบับ

deepcopy สร้างวัตถุผสมใหม่และแทรกสำเนาของวัตถุต้นฉบับของวัตถุสารประกอบต้นฉบับ

ลองยกตัวอย่าง

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

โค้ดด้านบนจะพิมพ์เป็น FALSE

มาดูกันว่า

วัตถุประกอบดั้งเดิมx=[1,[2]](เรียกว่าเป็นสารผสมเนื่องจากมีวัตถุอยู่ภายในวัตถุ (Inception))

ป้อนคำอธิบายรูปภาพที่นี่

อย่างที่คุณเห็นในภาพนั่นคือรายการภายในรายการ

y = copy.copy(x)จากนั้นเราก็สร้างสำเนาตื้นของมันโดยใช้ สิ่งที่หลามทำที่นี่คือมันจะสร้างวัตถุผสมใหม่ แต่วัตถุภายในพวกมันกำลังชี้ไปที่วัตถุดั้งเดิม

ป้อนคำอธิบายรูปภาพที่นี่

ในภาพมันได้สร้างสำเนาใหม่สำหรับรายการภายนอก แต่รายการภายในยังคงเหมือนเดิม

ตอนนี้เราสร้าง deepcopy z = copy.deepcopy(x)มันใช้ สิ่งที่หลามทำที่นี่คือมันจะสร้างวัตถุใหม่สำหรับรายการภายนอกเช่นเดียวกับรายการภายใน ตามที่แสดงในภาพด้านล่าง (เน้นสีแดง)

ป้อนคำอธิบายรูปภาพที่นี่

ที่สิ้นสุดรหัสพิมพ์Falseเป็น y และ z ไม่ใช่วัตถุเดียวกัน

HTH


2

การคัดลอกตื้นกำลังสร้างวัตถุใหม่แล้วคัดลอกเขตข้อมูลไม่คงที่ของวัตถุปัจจุบันไปยังวัตถุใหม่ ถ้าเขตข้อมูลเป็นประเภทค่า -> สำเนาของเขตข้อมูลจะดำเนินการ; สำหรับประเภทการอ้างอิง -> การอ้างอิงจะถูกคัดลอก แต่วัตถุที่อ้างอิงไม่ใช่; ดังนั้นวัตถุดั้งเดิมและโคลนอ้างอิงถึงวัตถุเดียวกัน

สำเนาลึกกำลังสร้างวัตถุใหม่แล้วคัดลอกเขตข้อมูลที่ไม่คงที่ของวัตถุปัจจุบันไปยังวัตถุใหม่ หากเขตข้อมูลเป็นประเภทค่า -> จะทำการคัดลอกฟิลด์เป็นบิต หากเขตข้อมูลเป็นประเภทการอ้างอิง -> จะทำการคัดลอกใหม่ของวัตถุที่อ้างอิง คลาสที่จะถูกโคลนต้องถูกแฟล็กเป็น [Serializable]


2

นำมาจาก [บล็อก]: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

สำเนาลึกเกี่ยวข้องกับการใช้เนื้อหาของวัตถุหนึ่งเพื่อสร้างอินสแตนซ์อื่นของคลาสเดียวกัน ในสำเนาลึกวัตถุทั้งสองอาจมีข้อมูลเหมือนกัน ht แต่วัตถุเป้าหมายจะมีบัฟเฟอร์และทรัพยากรของตัวเอง การทำลายของวัตถุใดวัตถุหนึ่งจะไม่ส่งผลกระทบต่อวัตถุที่เหลืออยู่ ผู้ประกอบการที่ได้รับมอบหมายมากเกินไปจะสร้างสำเนาของวัตถุที่ลึก

การคัดลอกตื้นนั้นเกี่ยวข้องกับการคัดลอกเนื้อหาของวัตถุหนึ่งไปยังอีกอินสแตนซ์ของคลาสเดียวกันดังนั้นจึงสร้างภาพสะท้อน เนื่องจากการคัดลอกการอ้างอิงและพอยน์เตอร์โดยตรงวัตถุทั้งสองจะแบ่งปันเนื้อหาที่มีอยู่ภายนอกเดียวกันของวัตถุอื่นเพื่อคาดเดาไม่ได้

คำอธิบาย:

การใช้ตัวสร้างการคัดลอกเราเพียงคัดลอกค่าข้อมูลสมาชิกโดยสมาชิก วิธีการคัดลอกนี้เรียกว่าการคัดลอกตื้น ถ้าวัตถุนั้นเป็นคลาสที่เรียบง่ายประกอบด้วยประเภทที่สร้างขึ้นในตัวและไม่มีตัวชี้สิ่งนี้จะเป็นที่ยอมรับ ฟังก์ชั่นนี้จะใช้ค่าและวัตถุและพฤติกรรมของมันจะไม่ถูกแก้ไขด้วยสำเนาตื้นเฉพาะที่อยู่ของพอยน์เตอร์ที่เป็นสมาชิกจะถูกคัดลอกและไม่ใช่ค่าที่อยู่ชี้ไป ค่าข้อมูลของวัตถุจะถูกเปลี่ยนแปลงโดยฟังก์ชันโดยไม่ตั้งใจ เมื่อฟังก์ชันไม่ได้อยู่ในขอบเขตสำเนาของวัตถุที่มีข้อมูลทั้งหมดจะถูกดึงออกจากสแต็ก

หากวัตถุมีพอยน์เตอร์ใด ๆ จำเป็นต้องดำเนินการสำเนาลึก ด้วยสำเนาลึกของวัตถุหน่วยความจำจะถูกจัดสรรสำหรับวัตถุในร้านค้าฟรีและองค์ประกอบที่ชี้ไปจะถูกคัดลอก สำเนาลึกใช้สำหรับวัตถุที่ส่งคืนจากฟังก์ชัน


2

หากต้องการเพิ่มคำตอบอื่น ๆ

  • การคัดลอกตื้นของวัตถุทำการคัดลอกโดยค่าสำหรับคุณสมบัติตามประเภทของค่าและคัดลอกโดยการอ้างอิงสำหรับคุณสมบัติตามประเภทการอ้างอิง
  • สำเนาลึกของวัตถุดำเนินการคัดลอกโดยค่าสำหรับคุณสมบัติตามประเภทค่าเช่นเดียวกับคัดลอกโดยค่าสำหรับคุณสมบัติอ้างอิงตามประเภทลึกลงไปในลำดับชั้น (ของประเภทอ้างอิง)

2

การคัดลอกตื้นจะไม่สร้างการอ้างอิงใหม่ แต่การทำสำเนาแบบลึกจะสร้างการอ้างอิงใหม่

นี่คือโปรแกรมที่จะอธิบายสำเนาลึกและตื้น

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

1

คัดลอก ararys:

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

โคลนตื้นหมายถึงเฉพาะคัดลอกหน่วยความจำที่ถูกโคลนอาร์เรย์

ถ้าอาร์เรย์ประกอบด้วยวัตถุประเภทค่าค่าที่จะถูกคัดลอก ;

ถ้าอาร์เรย์ประกอบด้วยชนิดอ้างอิงอ้างอิงเท่านั้นจะถูกคัดลอก - เพื่อให้เป็นผลให้มีสองอาร์เรย์ที่มีสมาชิกอ้างอิงวัตถุเดียวกัน

ในการสร้างสำเนาลึกที่ซึ่งชนิดการอ้างอิงถูกทำซ้ำคุณต้องวนซ้ำอาร์เรย์และโคลนแต่ละองค์ประกอบด้วยตนเอง


ฉันไม่รู้เกี่ยวกับภาษาอื่น แต่ใน C # / VB การคัดลอกอาร์เรย์ของประเภทค่าแบบตื้นไม่ได้คัดลอกค่า สองอาร์เรย์หมายถึงวัตถุเดียวกัน เพิ่มปุ่มลงในแบบฟอร์มและเพิ่มรหัสนี้เพื่อดู: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]); }
DeanOC

ถูกต้องฉันแก้ไขคำตอบของฉันให้แม่นยำยิ่งขึ้นโดยใช้โคลนในอาร์เรย์ คุณมีสิทธิ์อย่างแน่นอนที่ "การคัดลอกอาเรย์ประเภทค่าจะไม่คัดลอกค่า" แต่การใช้โคลนบนอาเรย์จะทำได้ ฉันพยายามอธิบายให้ลองทำดู ขอบคุณ
lukaszk

1

ฉันมาทำความเข้าใจจากบรรทัดต่อไปนี้

การคัดลอกตื้นคัดลอกฟิลด์ประเภทค่าวัตถุ(int, float, bool) ไปยังวัตถุเป้าหมายและประเภทการอ้างอิงของวัตถุ (สตริงคลาส ฯลฯ ) จะถูกคัดลอกเป็นการอ้างอิงในวัตถุเป้าหมาย ในประเภทการอ้างอิงเป้าหมายนี้จะชี้ไปที่ตำแหน่งหน่วยความจำของวัตถุต้นฉบับ

สำเนาลึกคัดลอกค่าของวัตถุและประเภทการอ้างอิงลงในสำเนาใหม่ที่สมบูรณ์ของวัตถุเป้าหมาย ซึ่งหมายความว่าทั้งประเภทค่าและประเภทการอ้างอิงจะถูกจัดสรรไปยังที่ตั้งหน่วยความจำใหม่


0

การเพิ่มคำจำกัดความทั้งหมดข้างต้นหนึ่งสำเนาที่ใช้กันอย่างแพร่หลายมากที่สุดนั้นอยู่ในตัวสร้างการคัดลอก (หรือตัวดำเนินการกำหนดค่ามากเกินไป) ของคลาส

Shallow copy -> คือเมื่อคุณไม่ได้ให้ตัวสร้างการคัดลอก ที่นี่เฉพาะวัตถุที่ได้รับการคัดลอก แต่ไม่ได้คัดลอกสมาชิกของคลาสทั้งหมด

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
}

0

ตัวสร้างการคัดลอกจะใช้ในการเริ่มต้นวัตถุใหม่ด้วยวัตถุที่สร้างขึ้นก่อนหน้าของชั้นเดียวกัน โดยคอมไพเลอร์เริ่มต้นเขียนสำเนาตื้น การคัดลอกตื้นทำงานได้ดีเมื่อการจัดสรรหน่วยความจำแบบไดนามิกไม่เกี่ยวข้องเนื่องจากเมื่อการจัดสรรหน่วยความจำแบบไดนามิกเกี่ยวข้องกับวัตถุทั้งสองจะชี้ไปยังตำแหน่งหน่วยความจำเดียวกันในฮีปดังนั้นเราจึงลบปัญหานี้เราเขียนสำเนาลึก ในหน่วยความจำ เพื่อที่จะอ่านรายละเอียดกับตัวอย่างที่สมบูรณ์และคำอธิบายที่คุณจะได้เห็นบทความc ++ ก่อสร้าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.