1
Ba vấn đề con trỏ trong lập trình hướng đối tượng C++ là gì?
2
Hữu Thiện30 đã đăng:

Bạn nào có thể giải thích ba vấn đề con trỏ gặp phải khi áp dụng phương pháp lập trình hướng đối tượng trong C++ được không ạ? Tại sao lại cần phải tránh "ba vấn đề con trỏ" này và hướng giải quyết của nó là gì? Thanks...

thêm bình luận...
3
xuans2huy510 đã đăng:

Khi lập trình với ngôn ngữ C++, bất cứ khi nào bạn xây dựng một lớp mà trong đó, có một hoặc nhiều thuộc tính sử dụng biến con trỏ, bạn sẽ phải đối mặt với một vấn đề gọi là ba vấn đề con trỏ (thuật ngữ tiếng Anh gọi là Rule Of Three).

Mình nghĩ sử dụng một ví dụ để bắt đầu sẽ dễ hình dung hơn, ví dụ chúng ta có lớp KhachHang gồm các thuộc tính sau:

class KhachHang {
    private:
        string ten; // Tên
        int tuoi; // Tuổi
        string *monAnUaThich; // Danh sách các món ăn ưa thích
};

Giả sử bạn khởi tạo hai khách hàng A và B ở hàm main() như sau:

KhachHang A;
KhachHang B;

Bây giờ bạn muốn sao chép thông tin từ khách hàng A (bao gồm tên, tuổi, món ăn ưa thích) sang khách hàng B, bạn sẽ nghĩ ngay sử dụng câu lệnh gán là xong:

B = A;

Nó chỉ xong khi lớp KhachHang của bạn không có biến con trỏ, trong trường hợp lớp KhachHang chứa biến con trỏ thì mọi chuyện không đơn giản như vậy,

Bạn hy vọng điều gì khi thực hiện sao chép thông tin từ đối tượng A sang đối tượng B? Có phải là bạn sẽ hy vọng trong khi sao chép, B cần một vùng nhớ mới, sau đó A sẽ đưa thông tin cho B và B lưu thông tin vào vùng nhớ mới đó của mình không, tức là sau khi sao chép xong, A và B chẳng còn liên quan gì đến nhau nữa, cho dù bạn có xóa A đi thì cũng không ảnh hưởng gì đến B cả, sao chép như vậy người ta gọi là Deep Copy (sao chép sâu). Còn trường hợp sau khi sao chép, B không được cấp phát vùng nhớ mới mà lại sử dụng chung vùng nhớ của A người ta gọi đó là Shallow Copy (sao chép cạn).

Khi bạn sử dụng câu lệnh gán B = A, nếu bạn không tự định nghĩa một phương thức gán (=) riêng, C++ sẽ sử dụng phương thức gán (operator =) mặc định của nó, mà phương thức gán mặc định này không làm việc hiệu quả đối với biến con trỏ, nói cách khác là nó không sao chép sâu như chúng ta mong muốn, nó chỉ có thể sao chép cạn và dĩ nhiên sẽ gây ra một số vấn đề nghiêm trọng bởi sự sao chép cạn này.

Để giải quyết chuyện này chúng ta cần ghi đè phương thức gán = trong C++, trong đó chúng ta tự khởi tạo vùng nhớ mới cho đối tượng B rồi thực hiện sao chép thông tin từ A sang B:

KhachHang& KhachHang::operator=(const KhachHang& A){
    /* DO SOMETHING */
}

Lúc này, sẽ an toàn và đúng như ý khi bạn sử dụng câu lệnh gán B = A;.

Tóm lại, ba vấn đề con trỏ bao gồm:

  • Gán đối tượng có thuộc tính biến là con trỏ (mình đã giải thích rất rõ ở ví dụ trên).
  • Sao chép đối tượng có thuộc tính là biến con trỏ (tương tự như trên).
  • Hủy đối tượng có thuộc tính là biến con trỏ (phương thức hủy mặc định của C++ một lần nữa không hiệu quả, không thể hủy bộ nhớ đã cấp phát cho biến con trỏ của đối tượng ở vùng nhớ Heap, bạn phải tự viết hàm hủy vùng nhớ này).

Hướng giải quyết ba vấn đề con trỏ:

Khi bạn xây dựng lớp và lớp của bạn có sử dụng biến con trỏ thì bạn nên tự tay định nghĩa lại các toán tử gán =, phương thức sao chép và phương thức hủy, không nên sử dụng toán tử gán và phương thức mặc định của C++ bởi vì chúng không làm việc hiệu quả đối với biến con trỏ, có thể làm cho chương trình bạn bị lỗi hoặc không hoạt động như bạn mong muốn.

đã bổ sung 5.7 năm trước bởi
xuans2huy510
thêm bình luận...
Bạn đang thắc mắc? Ghi câu hỏi của bạn và đăng ở chế độ cộng đồng (?)