Copy Constructor เป็น Constructor พิเศษที่สร้าง Object ใหม่โดยคัดลอกจาก Object เดียวกัน Class ที่มีอยู่แล้ว Signature มาตรฐาน:
ClassName(const ClassName& other);
เมื่อไหร่ถูกเรียก?
- Initialize Object จาก Object อื่น:
ClassName obj2 = obj1; - Pass Object โดย Value ให้ Function:
void func(ClassName obj); - Return Object โดย Value จาก Function:
ClassName func() { return obj; } - Brace Initialization:
ClassName obj2{obj1};
Default Copy Constructor (Compiler-Generated)
ถ้าไม่เขียนเอง Compiler จะสร้าง Member-wise Copy (Shallow Copy) คัดลอกทุก Member ตามลำดับ
- ✅ ใช้ได้: POD Types (int, double, etc.),
std::string,std::vector(มี Copy Constructor ของตัวเอง) - ❌ อันตราย: Raw Pointers, Dynamic Memory, Resource Handles (File, Socket, Mutex)
ตัวอย่างปัญหา Shallow Copy (Double Free)
#include <iostream>
class Line {
int* ptr; // Raw Pointer -> ต้อง Manage เอง
public:
Line(int len) : ptr(new int(len)) {
std::cout << "Constructor\n";
}
// ไม่มี Copy Constructor -> Compiler ให้ Shallow Copy (Copy Pointer Address)
// Line(const Line& other) : ptr(other.ptr) { } // Implicit
~Line() {
std::cout << "Destructor: delete ptr\n";
delete ptr;
}
int getLength() const { return *ptr; }
};
void display(Line obj) { // Pass by Value -> Copy Constructor เรียก
std::cout << "Length: " << obj.getLength() << '\n';
} // obj ทำลาย -> delete ptr
int main() {
Line line(10);
display(line); // Copy -> Shallow Copy
// line.ptr ถูก Delete แล้ว!
// line.getLength() -> Use After Free (UB)
// main จบ -> line ทำลายอีกครั้ง -> Double Free (Crash)
}
แก้ไขด้วย Deep Copy Constructor
class Line {
int* ptr;
public:
Line(int len) : ptr(new int(len)) {}
// Deep Copy Constructor
Line(const Line& other) : ptr(new int(*other.ptr)) {
std::cout << "Deep Copy Constructor\n";
}
// Copy Assignment Operator (Rule of Three)
Line& operator=(const Line& other) {
if (this != &other) { // Self-assignment Check
delete ptr;
ptr = new int(*other.ptr);
}
return *this;
}
~Line() { delete ptr; }
int getLength() const { return *ptr; }
};
Rule of Three / Five / Zero
| Rule | Functions | เมื่อไหร่ใช้ |
|---|---|---|
| Rule of Three (C++98) | Destructor, Copy Ctor, Copy Assignment | Manage Resource แบบ Manual (Raw Pointer) |
| Rule of Five (C++11) | + Move Ctor, Move Assignment | รองรับ Move Semantics |
| Rule of Zero (Modern) | ไม่เขียนอะไรเลย | ใช้ RAII Types (unique_ptr, vector, string) จัดการ Resource ให้เอง |
Modern C++: Rule of Zero (แนะนำ)
#include <memory>
#include <vector>
class ModernLine {
std::unique_ptr<int> ptr; // หรือ std::vector ptr;
public:
ModernLine(int len) : ptr(std::make_unique<int>(len)) {}
// ไม่ต้องเขียน Destructor, Copy, Move - Compiler สร้างให้ถูกต้อง!
// unique_ptr: Non-copyable, Movable (Transfer Ownership)
// vector: Deep Copy โดยอัตโนมัติ
};
สรุป
Copy Constructor จำเป็นเมื่อ Class จัดการ Resource เอง (Raw Pointer) ให้เขียน Deep Copy พร้อม Copy Assignment และ Destructor (Rule of Three) แต่ใน Modern C++ ควรใช้ Rule of Zero: ใช้ RAII Containers/Smart Pointers ให้ Compiler Generate Special Member Functions ให้โดยอัตโนมัติ