Copy Constructor เป็น Constructor พิเศษที่สร้าง Object ใหม่โดย คัดลอกจาก Object เดิมของคลาสเดียวกัน
ลายเซ็นมาตรฐาน
ClassName(const ClassName& other);
ต้องรับ const reference เพื่อป้องกัน Infinite Recursion (ถ้ารับ by value จะต้อง copy อีกครั้ง)
โค้ดตัวอย่าง
#include <iostream>
using namespace std;
class Demo {
private:
int num1, num2;
public:
// Normal Constructor
Demo(int n1, int n2) : num1(n1), num2(n2) {
cout << "Parameterized Constructor" << endl;
}
// Copy Constructor
Demo(const Demo& other) : num1(other.num1), num2(other.num2) {
cout << "Copy Constructor called" << endl;
}
void display() const {
cout << "num1 = " << num1 << ", num2 = " << num2 << endl;
}
};
int main() {
Demo obj1(10, 20); // Parameterized Constructor
Demo obj2 = obj1; // Copy Constructor (Copy Initialization)
Demo obj3(obj1); // Copy Constructor (Direct Initialization)
obj1.display();
obj2.display();
obj3.display();
return 0;
}
ผลลัพธ์
Parameterized Constructor Copy Constructor called Copy Constructor called num1 = 10, num2 = 20 num1 = 10, num2 = 20 num1 = 10, num2 = 20
เมื่อไหร่ Copy Constructor ถูกเรียก?
| สถานการณ์ | ตัวอย่าง |
|---|---|
| Copy Initialization | Type obj2 = obj1; |
| Direct Initialization | Type obj2(obj1); |
| Pass by Value to Function | void func(Type obj); func(obj1); |
| Return by Value from Function | Type func() { return obj; } |
| Initialize in Container | vector<Type> v; v.push_back(obj); |
Shallow Copy vs Deep Copy
Shallow Copy (Default)
คัดลอกค่า Member ทุกตัวแบบ Bitwise
class Shallow {
int* data;
public:
Shallow(int v) { data = new int(v); }
// Compiler-generated copy constructor
// data ชี้ไปที่เดียวกัน!
};
❌ ปัญหา: Double Free, Dangling Pointer
Deep Copy (Manual)
จอง Memory ใหม่ และคัดลอกข้อมูล
class Deep {
int* data;
public:
Deep(int v) { data = new int(v); }
Deep(const Deep& other) {
data = new int(*other.data); // Deep copy
}
~Deep() { delete data; }
};
✓ ปลอดภัย: แต่ละ Object มี Memory ของตัวเอง
Default Copy Constructor (Compiler-generated)
- ทำ Member-wise Shallow Copy
- เหมาะสำหรับ Class ที่มีแค่ Primitive Types หรือ RAII Types (string, vector, smart pointers)
- ไม่เหมาะ สำหรับ Raw Pointers, File Handles, Socket Handles
การปิดกั้น Copy (Non-copyable)
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete; // ห้าม Copy Construct
NonCopyable& operator=(const NonCopyable&) = delete; // ห้าม Copy Assign
};
// หรือสืบทอดจาก base
class NonCopyableBase {
protected:
NonCopyableBase() = default;
~NonCopyableBase() = default;
NonCopyableBase(const NonCopyableBase&) = delete;
NonCopyableBase& operator=(const NonCopyableBase&) = delete;
};
Rule of 3/5/0 (เชื่อมโยงกับ Destructor)
- ถ้าเขียน Destructor เอง → ควรเขียน Copy Constructor และ Copy Assignment ด้วย (Rule of 3)
- C++11: เพิ่ม Move Constructor และ Move Assignment (Rule of 5)
- Modern C++: ใช้ RAII Types → ไม่ต้องเขียนอะไร (Rule of 0)
Copy Elision (Optimization)
Compiler อาจ ข้าม Copy Constructor ได้ (C++17 บังคับบางกรณี)
Demo createDemo() {
return Demo(10, 20); // อาจสร้างตรงใน obj3 (NRVO)
}
int main() {
Demo obj3 = createDemo(); // อาจมีเพียง 1 Constructor call
}