Đang tải...

[C++ OOP Thực Chiến] Bài 36: Đặc tính cơ bản của kế thừa đơn (Phần 2) - Cú lừa của private và sự xuất hiện của protected!

14/05/2026
5 phút đọc
[C++ OOP Thực Chiến] Bài 36: Đặc tính cơ bản của kế thừa đơn (Phần 2) - Cú lừa của private và sự xuất hiện của protected!
Chào anh em! Ở cuối Bài 35, chúng ta đã để hở một lỗ hổng bảo mật chết người: Để Lớp con (`DevBackend`) có thể xài được biến `hoTen` và `luongCoBan` của ...

Chào anh em! Ở cuối Bài 35, chúng ta đã để hở một lỗ hổng bảo mật chết người: Để Lớp con (DevBackend) có thể xài được biến hoTenluongCoBan của Lớp cha (NhanVien), chúng ta đã ngậm ngùi ném hai biến đó ra khu vực public.

Điều này đồng nghĩa với việc vứt bỏ hoàn toàn công sức xây dựng tính Đóng gói (Encapsulation) từ đầu series đến giờ. Ở ngoài hàm main, ai cũng có thể gọi dev.luongCoBan = 99999; để tự tăng lương cho mình.

Nếu chúng ta đưa chúng về lại private thì sao? Một rắc rối lớn hơn sẽ xuất hiện.

1. Sự khắc nghiệt của bức tường private

Nhiều người mới học C++ thường lầm tưởng: "Lớp con kế thừa Lớp cha thì đương nhiên nó được xài hết tài sản của cha, kể cả private!"

Đây là một cú lừa! Trong C++, private là một lãnh địa BẤT KHẢ XÂM PHẠM. Nó thuộc về quyền sở hữu độc quyền của Class định nghĩa ra nó. Khi Lớp con kế thừa Lớp cha, nó VẪN NHẬN ĐƯỢC biến private đó (biến đó vẫn tồn tại trong RAM khi đúc Object con), nhưng Lớp con BỊ CẤM CHẠM VÀO.

Giống như việc cha bạn để lại cho bạn một cái két sắt (private). Két sắt đó nằm trong nhà bạn, nhưng cha bạn không cho mã PIN. Bạn không thể tự mở nó ra được!

Nếu DevBackend cố tình gọi hoTen (đang là private của NhanVien): Trình biên dịch sẽ báo lỗi đỏ lòm: error: 'hoTen' is a private member of 'NhanVien'.

2. Vị cứu tinh mang tên protected

Để giải quyết bài toán "Tiến thoái lưỡng nan" này (Để public thì mất an toàn, để private thì con cái không dùng được), OOP sinh ra một Access Modifier thứ 3: protected (Được bảo vệ).

Hãy coi protected là "Bảo vật truyền gia":

  • Đối với người ngoài (hàm main, các Class khác): Nó đóng sập cửa, hoạt động khép kín y hệt như private. Không ai ở ngoài có thể truy cập được.
  • Đối với con cháu (Lớp kế thừa): Nó mở toang cửa, hoạt động thoải mái y hệt như public.

Đây là từ khóa sinh ra DÀNH RIÊNG cho Kế thừa!

3. Nghệ thuật phân quyền trong hệ thống Backend

Trong thực tế, khi thiết kế kiến trúc hệ thống, một kỹ sư cứng cáp sẽ biết cách kết hợp cả privateprotected trong cùng một Lớp cha để phân quyền dữ liệu.

Ví dụ: Bạn đang thiết kế hệ thống quản lý nhân sự.

  • Tên, Email, Chức vụ: Có thể cho phép Lớp con truy cập để tái sử dụng in log, hiển thị thông tin -> Dùng protected.
  • Lương cơ bản, Mật khẩu: Là dữ liệu cực kỳ nhạy cảm. Kể cả Lớp con cũng không được phép tự ý sửa đổi bừa bãi. Bắt buộc phải là private. Nếu con muốn xem hoặc đổi, phải dùng Getter/Setter của cha!

Hãy xem sự lợi hại của kiến trúc này trong code:

#include <iostream>
#include <string>

using namespace std;

// --- LỚP CHA ---
class NhanVien {
private:
 double luongCoBan; // Cấm tuyệt đối! Con cái cũng không được đụng vào.

protected:
 string hoTen; // Bảo vật truyền gia: Con cái được phép dùng.

public:
 NhanVien(string ten, double luong) : hoTen(ten), luongCoBan(luong) {}

 // Cung cấp Getter để Lớp con có thể "xem" lương một cách hợp pháp
 double getLuong() const { return luongCoBan; } 
};

// --- LỚP CON ---
class DevBackend : public NhanVien {
private:
 string ngonNguCode;

public:
 DevBackend(string ten, double luong, string ngonNgu) 
 : NhanVien(ten, luong) 
 {
 ngonNguCode = ngonNgu;
 }

 void inThongTin() {
 // Truy cập TRỰC TIẾP hoTen vì nó là protected
 cout << "[Info] Dev: " << hoTen << "\n";
 
 cout << "[Info] Ngon ngu: " << ngonNguCode << "\n";

 // Bị CẤM truy cập trực tiếp luongCoBan vì nó là private của cha
 // cout << luongCoBan; // -> BÁO LỖI NGAY!

 // Phải truy cập GIÁN TIẾP qua cửa chính Getter
 cout << "[Info] Luong: $" << getLuong() << "\n"; 
 }
};

int main() {
 cout << "--- HE THONG PHAN QUYEN KẾ THỪA ---\n\n";

 DevBackend dev("Hieu", 2500.0, "Golang & PHP");
 dev.inThongTin();

 // Hacker ở ngoài main cố tình phá hoại:
 // dev.hoTen = "Hacker"; // LỖI: protected chặn người ngoài
 // dev.luongCoBan = 99999.0; // LỖI: private chặn người ngoài

 return 0;
}

Nhận xét: Tuyệt vời! Kiến trúc của chúng ta giờ đây vững như bàn thạch. Tính Đóng gói (Encapsulation) được bảo toàn 100%, mà tính Tái sử dụng (Inheritance) vẫn hoạt động vô cùng trơn tru. Hệ thống phân định rõ ràng ranh giới: Cái gì thuộc về "mật" của cha thì con chỉ được nhìn qua kính (Getter), cái gì là "truyền thống" của gia đình thì con được quyền cầm nắm (protected).

Tạm kết & Gợi mở

Anh em đã nắm trong tay bộ 3 quyền lực nhất của OOP: private (Chỉ mình ta), protected (Cho con cháu), và public (Cho thiên hạ).

Nhưng nhìn vào hàm Constructor của Lớp con DevBackend ở trên, có lẽ anh em sẽ thấy một cú pháp khá lạ lẫm: : NhanVien(ten, luong). Tại sao Lớp con khi nhận tham số vào, lại phải "đá" ngược tham số đó lên cho Lớp cha xử lý? Khi một Object Lớp con được new ra trong RAM, phần bộ nhớ của cha hay của con sẽ được khởi tạo trước? Và khi Object đó bị hủy đi, Destructor nào sẽ chạy trước để dọn rác?

Vòng đời của Object trong Kế thừa phức tạp hơn những gì chúng ta tưởng. Hẹn gặp lại anh em ở Bài 37: Cài đặt kế thừa (Phần 1) - Cuộc chiến Constructor và định lý quả trứng con gà!

📚 Nguồn: Viblo

Chia sẻ bài viết

Cần tư vấn?

Liên hệ với chúng tôi để được hỗ trợ

Liên hệ ngay

Bài viết liên quan

Giải mã RdKafka: "Trùm cuối" ẩn mình giúp PHP giao tiếp với Kafka siêu tốc
15/05/2026

Giải mã RdKafka: "Trùm cuối" ẩn mình giúp PHP giao tiếp với Kafka siêu tốc

Chào anh em cộng đồng Viblo! Nếu anh em làm Kafka với PHP hoặc Laravel, chắc chắn anh em đã từng phải chạy cái lệnh này trên server hoặc trong Dockerfile: `pecl install...

Đọc thêm
Từ Lý Thuyết Đến Thực Tế: Thiết Kế Module Invoice Nâng Cao - 3 Dạng Tính Điện/Nước, Dịch Vụ Động, và InvoiceCalculator Thuần Túy không phụ thuộc database
15/05/2026

Từ Lý Thuyết Đến Thực Tế: Thiết Kế Module Invoice Nâng Cao - 3 Dạng Tính Điện/Nước, Dịch Vụ Động, và InvoiceCalculator Thuần Túy không phụ thuộc database

Tiếp theo series Từ Lý Thuyết Đến Thực Tế, bài này mình viết câu chuyện thiết kế Module Invoice Nâng Cao, chúng ta cùng đi trả lời các câu hỏi: - Tại sao phải...

Đọc thêm
Đừng bao giờ Hardcode tên Kafka Topic: Bí mật "sống còn" đằng sau file .env.example
15/05/2026

Đừng bao giờ Hardcode tên Kafka Topic: Bí mật "sống còn" đằng sau file .env.example

Chào anh em cộng đồng Viblo! Trong các bài viết trước về Kafka, để code ngắn gọn và dễ hiểu, mình thường viết thẳng tên Topic vào hàm như thế này: `Kafka::pub...

Đọc thêm

Bắt đầu dự án của bạn

Hãy để Flash Dev đồng hành cùng bạn

Liên hệ ngay