Copy constructor

ภูวเดช นามไพร 650710578

Constructor :

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

Syntax:

class Person {
    public string Name { get; set; }
    public int Age { get; set;}
    
    //Parameterized Constructor
    public Person(string name, int age) {
        Name = name;
        Age = age;
    }
}

Copy Constructor:

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

Syntax:

class Person {
    public string Name { get; set; }
    public int Age { get; set; }
    
    //copy constructor
    public Person(Person other) {
        Name = other.Name;
        Age = other.Age;
    }
}

ตัวอย่าง:

// C# Program to illustrate the use
// of Copy constructor
using System;
namespace copyConstructorExample {
class Vehicle {
	// variables
	private string name;
	private string color;
	private int quantity;

	// Copy constructor
	public Vehicle(Vehicle a)
	{
		name = a.name;
		color = a.color;
		quantity = a.quantity;
	}

	// Parameterized constructor
	public Vehicle(string name, string color, int quantity)
	{
		this.name = name;
		this.color = color;
		this.quantity = quantity;
	}

	// Get details of Vehicles
	public string DetailsofVehicle{
		get{
			return "Type: " + name.ToString() + 
				"\nColor: " + color.ToString() + 
				"\nQuantity: " + quantity.ToString();
		}
	}

	// Main Method
	public static void Main(){

		// Create a new object.
		Vehicle v1 = new Vehicle("Bike", "Black", 40);

		// here is v1 details are copied to v2.
		Vehicle v2 = new Vehicle(v1);

		Console.WriteLine(v2.DetailsofVehicle);
	}
}
}
ผลลัพธ์
Type: Bike
Color: Black
Quantity: 40

ความแตกต่างระหว่าง Shallow Copy และ Deep Copy

Shallow copy :

  • คัดลอกแค่ "ที่อยู่" ของข้อมูล ไม่ได้คัดลอกข้อมูลจริงๆ

  • เหมาะสำหรับวัตถุที่ไม่ซับซ้อน หรือเมื่อต้องการให้วัตถุที่คัดลอกมาอ้างอิงข้อมูลเดียวกับวัตถุต้นฉบับ เร็วกว่า deep copy เนื่องจากไม่มีการจัดสรรหน่วยความจําใหม่

class ShallowCopy :  ICloneable
{
    public int I {get;set;}
    public int J {get;set;}
    //method for cloning object
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}
class Demo
{
    public static void Main()
    {
        ShallowCopy obj=new ShallowCopy();
        Console.WriteLine(“--------before Shellow Clopy------”);
        ShallowCopy objClone=obj;
        obj.I=10;// setting obj value after cloning..
        Console.WriteLine(“objvalue : {0} \t Clone value : {1}”,obj.I,objClone.I=10);
        Console.WriteLine(“--------after Shellow Copy------”);
        ShallowCopy objClone2=(ShallowCopy)obj.Clone();  // cast object to //ShallowCopy
        obj.I=1000;  // MemberwiseClone() will not use this reference..
        Console.WriteLine(“after using MemberwiseClone() Clone() method :{0}”,objClone2.I);
    }
}
ผลลัพธ์
--------before Shellow Clopy------
objvalue : 0    Clone value : 0
--------after Shellow Copy------
after using MemberwiseClone() Clone() method : 10

Deep copy:

  • คัดลอกทั้ง "ที่อยู่" และ "ข้อมูล" ทำให้ได้วัตถุใหม่ที่แยกจากกันจริงๆ

  • เหมาะสำหรับวัตถุที่ซับซ้อน มีหลายชั้น หรือเมื่อต้องการให้วัตถุที่คัดลอกมาเป็นอิสระจากวัตถุต้นฉบับ แต่จะช้ากว่า shallow copy เนื่องจากมีการจัดสรรหน่วยความจําใหม่

class ReferenceType
{
    public int RFT {  get;  set; }
}
class ShallowCopy :  ICloneable
{
    public int I {  get;  set; }
    public int J {  get;  set; }
    public ReferenceType K =  new ReferenceType();
    //Method updated for reference type ..
    public object Clone()
    {
        // Shalllow Copy..
        ShallowCopy SC = (ShallowCopy)this.MemberwiseClone();
        // Deep copy...
        ReferenceType RT =  new ReferenceType();
        RT.RFT =  this.K.RFT;
        SC.K = RT;
        return SC;
    }
    public static void Main(String[] args)
    {
        ShallowCopy obj =  new ShallowCopy();
        obj.K.RFT = 100;
        ShallowCopy objclone = (ShallowCopy)obj.Clone();
        obj.K.RFT = 200;  // make changes in obj.
        Console.WriteLine(objclone.K.RFT);
    }
}
ผลลัพธ์
100

เปรียบเทียบ copy constructor ของ C# กับภาษา Java/C/Python

C:

ภาษา C ไม่มี Copy Constructor เพราะไม่ได้เป็นภาษาเชิงวัตถุ (object-oriented) แบบเต็มรูปแบบ แต่สามารถทำการคัดลอก struct หรือ object แบบทำเองได้โดยใช้การคัดลอก field ทีละตัว หรือใช้ฟังก์ชัน

Shallow copy:

การคัดลอก primitive types จากตัวแปรหนึ่งไปยังอีกตัวแปรหนึ่ง แต่ถ้าตัวแปรนั้นเป็น pointer จะทำการคัดลอกเพียง address ของ object ที่ pointer นั้นชี้อยู่ ดังนั้นทั้งต้นฉบับและตัวคัดลอกจะใช้ object เดียวกันในหน่วยความจำ

#include <stdlib.h>
#include <string.h>

typedef struct student
{
	char *name;
	int age;
}student;
int main()
{
	student t1;
	t1.name = (char*)malloc(30);
	strcpy(t1.name, "John");
	t1.age = 22;
	student t2 = t1;
	printf("%s %d", t2.name, t2.age);
	if (t1.name != NULL) 
	{
		free(t1.name);
		t1.name = NULL;
	}                 

	return 0;
}
ผลลัพธ์
John 22

Deep copy:

การคัดลอกทั้งตัวแปร primitive และ object ที่ pointer ชี้ไป โดยการจัดสรร allocate new memory และคัดลอกข้อมูลที่อยู่ในหน่วยความจำเก่าไปยังหน่วยความจำใหม่ ทำให้ object ที่คัดลอกมามีความเป็นอิสระจาก object ต้นฉบับ

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct Student
{
    char *name;
    int age;
}Student;
 
int main(int argc, char *argv[])
{
    Student std1;
    Student std2;
 
    std1.name = (char *)malloc(10);
    std1.age = 20;
 
    strcpy(std1.name, "John");
 
    printf("std1->name: %s, age: %d\n", std1.name, std1.age);

    std2 = std1;
    std2.name = (char *)malloc(10);        
    strcpy(std2.name, std1.name);           
    printf("std2->name: %s, age: %d\n", std2.name, std2.age);
 
    free(std1.name);      

    free(std2.name);      
 
    return 0;
}
ผลลัพธ์
Jstd1->name: John, age: 20
std2->name: John, age: 20

ตัวอย่าง C เทียบ copy constructor กับ C#

C#:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(Person other) // Copy constructor
    {
        Name = other.Name;
        Age = other.Age;
    }
}

C:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char *name;
    int age;
} Person;

Person* copyPerson(Person *person) {
    Person *newPerson = (Person*)malloc(sizeof(Person));
    if (newPerson == NULL) {
        return NULL;
    }
    newPerson->name = (char*)malloc(strlen(person->name) + 1);
    strcpy(newPerson->name, person->name);
    newPerson->age = person->age;
    return newPerson;
}

Java:

ภาษา Java ไม่มี constructor สำหรับการคัดลอกโดยตรงเหมือนใน C++ หรือ C# แต่สามารถสร้าง Copy Constructor เองได้ โดยจะเป็นวิธีการสร้างวัตถุใหม่ โดยการคัดลอกค่าจากวัตถุที่มีอยู่แล้วมาใช้

Shallow copy:

การคัดลอกเฉพาะ primitive types และreferences ของ object แต่ไม่คัดลอกตัว object ที่ถูกอ้างอิง ทำให้ object ที่คัดลอกทั้งคู่ชี้ไปยัง object เดียวกันในหน่วยความจำ

class ABC  
{  
    // Instance variable of the class ABC  
    int x = 30;  
}  

public class ShallowCopyExample   
{     
    // Main method  
    public static void main(String argvs[])   
    {  
        // Creating an object of the class ABC  
        ABC obj1 = new ABC();  
        
        // It will copy the reference, not the value  
        ABC obj2 = obj1;  
        
        // Updating the value to 6 using the reference variable obj2  
        obj2.x = 6;  
        
        // Printing the value of x using reference variable obj1   
        System.out.println("The value of x is: " + obj1.x);  
    }  
}
ผลลัพธ์
The value of x is: 6

Deep copy:

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

class ABC  
{  
    // Instance variable of the class ABC  
    int x = 30;  
}  

public class DeepCopyExample   
{     
    // Main method  
    public static void main(String argvs[])   
    {  
        // Creating an object of the class ABC  
        ABC obj1 = new ABC();  
        
        // Creating another object of the class ABC for deep copy  
        ABC obj2 = new ABC();  
        
        // Updating the value to 6 using the reference variable obj2  
        obj2.x = 6;  
        
        // Printing the value of x using reference variable obj1   
        System.out.println("The value of x is: " + obj1.x);  
    }  
}
ผลลัพธ์
The value of x is: 30

ตัวอย่าง Java เทียบ copy constructor กับ C#

C#:

class Person {
    public string Name { get; set; }
    public int Age { get; set; }

    // Copy Constructor
    public Person(Person other) {
        Name = other.Name;
        Age = other.Age;
    }
}

Java:

class Person {
    String name;
    int age;

    // Copy Constructor
    public Person(Person other) {
        this.name = other.name;
        this.age = other.age;
    }
}

Python:

ภาษา Python ไม่มี copy constructor แบบที่พบใน C++ หรือ Java แต่วิธีการสร้างวัตถุใหม่โดยใช้ข้อมูลจากวัตถุที่มีอยู่แล้ว ใน Python, __init__ method ทำหน้าที่เป็นตัวสร้างวัตถุ และ Copy Constructor สามารถสร้างได้โดยการกำหนดวิธีการที่มีรูปแบบเฉพาะ

Shallow copy:

สร้างวัตถุใหม่ แต่แทนที่จะคัดลอกองค์ประกอบของวัตถุดั้งเดิม จะคัดลอกอ้างอิงไปยังวัตถุเหล่านั้น ซึ่งหมายความว่าการเปลี่ยนแปลงที่ทำกับวัตถุที่ฝังอยู่ (nested objects) ในวัตถุที่คัดลอกมาจะสะท้อนไปยังวัตถุดั้งเดิมและในทางกลับกัน ใน Python โมดูล copy และมีเมธอด copy()

import copy

class ShallowCopyExample:
    def __init__(self, data):
        self.data = data

    def __str__(self):
        return f&quot;Original Object: {self.data}&quot;

    def shallow_copy(self):
        return copy.copy(self)

# Example usage
original_object = ShallowCopyExample([1, 2, 3])
copy_object = original_object.shallow_copy()

print(original_object)
print(copy_object)

# Modify the copied object
copy_object.data.append(4)

# Changes reflected in the original object
print(original_object)
print(copy_object)
ผลลัพธ์
Original Object: [1, 2, 3]
Original Object: [1, 2, 3]
Original Object: [1, 2, 3, 4]
Original Object: [1, 2, 3, 4]

Deep copy:

โดยสร้างวัตถุใหม่และคัดลอกวัตถุย่อยทั้งหมดแบบเรียกซ้ำ (recursively) เพื่อให้แน่ใจว่าการเปลี่ยนแปลงในวัตถุที่คัดลอกมาไม่ส่งผลกระทบต่อวัตถุดั้งเดิม โมดูล copy ของ Python และยังมีเมธอด deepcopy()

import copy

class DeepCopyExample:
    def __init__(self, data):
        self.data = data

    def __str__(self):
        return f&quot;Original Object: {self.data}&quot;

    def deep_copy(self):
        return copy.deepcopy(self)

# Example usage
original_object = DeepCopyExample([1, [2, 3], 4])
copy_object = original_object.deep_copy()

print(original_object)
print(copy_object)

# Modify the copied object
copy_object.data[1].append(99)

# Changes not reflected in the original object
print(original_object)
print(copy_object)
ผลลัพธ์
Original Object: [1, [2, 3], 4]
Original Object: [1, [2, 3], 4]
Original Object: [1, [2, 3], 4]
Original Object: [1, [2, 3, 99], 4]

ตัวอย่าง Python เทียบ copy constructor กับ C#

C#:

class Person {
    public string Name { get; set; }
    public int Age { get; set; }

    // Copy Constructor
    public Person(Person other) {
        Name = other.Name;
        Age = other.Age;
    }
}

Person person1 = new Person { Name = "Alice", Age = 30 };
Person person2 = new Person(person1);  // Copy constructor

Python:

import copy

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# Shallow Copy
person1 = Person("Alice", 30)
person2 = copy.copy(person1)  # Shallow copy

# Deep Copy
person3 = copy.deepcopy(person1)  # Deep copy

Video:

Slide:

Reference:

w3schools. (n.d.). C# Constructors.

ankita_saini. (04 Dec, 2021). C# | Copy Constructor.

Anupam Singh. (04 Feb, 2022).Shallow Copy and Deep Copy Using C#.

OpenGenus Tech Review Team. (n.d.). Copy struct in C [Shallow & Deep Copy].

Javatpoint. (n.d.). Shallow Copy Vs. Deep Copy in Java.

GeeksforGeeks.(13 Feb, 2024). Copy Constructor in Python.

Microsoft. (03/12/2024). How to write a copy constructor (C# Programming Guide).

Last updated