Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Copy Constructor trong C++: Khi nào được gọi?

Copy Constructor là constructor đặc biệt khởi tạo đối tượng mới từ đối tượng cùng loại đã tồn tại. Dấu hiệu nhận biết: tham số là lvalue reference const của cùng lớp.

Cú pháp chuẩn

ClassName(const ClassName& other); // const lvalue reference
// Hoặc (hiếm): ClassName(ClassName& other); // non-const – không bind được temporary

3 trường hợp GỌI COPY CONSTRUCTOR

  1. Khởi tạo đối tượng từ đối tượng khác:
    Line line1(10);
    Line line2 = line1;     // copy init -> gọi copy ctor
    Line line3(line1);      // direct init -> gọi copy ctor
  2. Truyền đối tượng theo giá trị (by value) vào hàm:
    void display(Line obj); // tham số by value
    display(line1);           // copy line1 -> obj
  3. Trả về đối tượng theo giá trị từ hàm (trước C++17, hoặc khi NRVO không áp dụng):
    Line createLine() {
        Line l(20);
        return l; // có thể gọi copy ctor (hoặc move/elide)
    }

Ví dụ đầy đủ – theo dõi lời gọi

#include <iostream>
using namespace std;

class Line {
    int *ptr;
public:
    Line(int len) { cout << "Normal ctor\n"; ptr = new int(len); }
    Line(const Line& obj) { cout << "Copy ctor\n"; ptr = new int(*obj.ptr); }
    ~Line() { cout << "Dtor\n"; delete ptr; }
    int getLength() const { return *ptr; }
};

void display(Line obj) { // by value
    cout << "Length: " << obj.getLength() << endl;
}

int main() {
    Line line1(10);       // Normal ctor
    display(line1);       // Copy ctor (by value)
    Line line2 = line1;   // Copy ctor (copy init)
    display(line2);       // Copy ctor
    return 0;
}

Kết quả

Normal ctor
Copy ctor
Length: 10
Copy ctor
Copy ctor
Length: 10
Dtor
Dtor
Dtor
Dtor

Shallow Copy vs Deep Copy

  • Shallow copy (default compiler-generated): copy bitwise các thành viên – con trỏ trỏ cùng địa chỉ → double free, use-after-free.
  • Deep copy (tự viết): cấp phát mới, copy dữ liệu trỏ đến – như ví dụ trên.
  • Rule of 3/5/0: Nếu cần custom destructor/copy/move → cần đủ 3/5. C++11: thêm move constructor/assignment. C++20: = default.

Copy Elision & RVO/NRVO (C++17)

  • Trình biên dịch có quyền bỏ qua copy/move khi trả về local object hoặc khởi tạo từ temporary.
  • C++17: Guaranteed copy elision cho prvalue initialization (Line l = Line(10); – không gọi copy/move).
  • Vẫn phải khai báo copy/move constructor accessible (public) ngay cả khi elided.

Best Practice

  • Nếu class quản lý tài nguyên (con trỏ, handle, socket...) → tự viết deep copy (hoặc delete copy, chỉ cho move).
  • Dùng std::unique_ptr, std::shared_ptr, std::vector, std::string – chúng đã lo copy đúng.
  • Mặc định: = default hoặc không khai báo (compiler-generated) cho class POD-like.
  • Truyền đối tượng lớn: const reference (void func(const Line&)) tránh copy.