[C++ OOP Thực Chiến] Bài 40: Phạm vi truy xuất trong Kế thừa - Những cánh cửa vô hình!
Chào anh em! Từ [Bài 35] đến giờ, mỗi khi cho Lớp Con kế thừa Lớp Cha, chúng ta đều viết một cú pháp quen thuộc:
class DevBackend : public NhanVien
Cái chữ public màu xanh mỏng manh đó thực chất là một Bộ lọc quyền hạn (Access Specifier). Nó đứng đó để quyết định xem những tài sản (thuộc tính, phương thức) mà Lớp Cha truyền xuống cho Lớp Con sẽ bị "bóp méo" hay "hạ cấp" quyền hạn như thế nào khi nằm trong tay Lớp Con.
C++ cung cấp 3 loại màng lọc kế thừa: public, protected, và private.
1. Định lý Bất khả xâm phạm: Két sắt private của Cha
Trước khi đi vào các màng lọc, anh em phải khắc cốt ghi tâm một quy luật tối cao đã học ở [Bài 36]:
Thuộc tính private của Lớp Cha thì Lớp Con KHÔNG BAO GIỜ chạm tới được, bất chấp việc bạn dùng màng lọc kế thừa nào!
Các màng lọc kế thừa (public, protected, private) CHỈ CÓ TÁC DỤNG lên những tài sản thuộc diện public và protected của Lớp Cha mà thôi. Bản chất của các màng lọc này là hạ cấp quyền truy cập để siết chặt bảo mật.
2. Kế thừa public: Cánh cửa trong suốt (Giữ nguyên hiện trạng)
Đây là màng lọc phổ biến nhất (chiếm 99% các trường hợp thực tế) và là nền tảng cho mối quan hệ IS-A chuẩn mực.
Khi dùng class Con : public Cha:
publiccủa Cha truyền xuống Con Vẫn làpubliccủa Con.protectedcủa Cha truyền xuống Con Vẫn làprotectedcủa Con.
Ví dụ: Hàm chamCong() của NhanVien là public. Lớp DevBackend kế thừa public, nên ở ngoài hàm main(), người ta vẫn có thể gọi dev.chamCong(). Mọi thứ trơn tru, đúng bản chất logic.
3. Kế thừa protected và private: Cánh cửa hạ cấp quyền lực
Chuyện gì xảy ra nếu bạn cố tình thắt chặt màng lọc?
A. Kế thừa protected (class Con : protected Cha)
Nó hạ cấp mọi thứ mở toang (public) của Cha xuống thành protected trong Lớp Con.
- Hàm
chamCong()vốn làpublicở Cha, nhưng khi truyền xuống Con, nó bị biến thànhprotected. - Hệ quả: Trong hàm
main(), bạn KHÔNG THỂ gọidev.chamCong()được nữa! Nhưng nếuDevBackendđẻ ra thêm Lớp Cháu, thì Lớp Cháu vẫn có thể gọi hàm đó ngầm bên trong.
B. Kế thừa private (class Con : private Cha)
Đây là màng lọc cực đoan nhất. Nó gom TẤT CẢ mọi thứ (public, protected) của Cha và nén chặt thành private của Lớp Con.
- Hệ quả 1: Ngoài hàm
main(), không ai gọi đượcdev.chamCong(). - Hệ quả 2 (Tuyệt tự): Nếu
DevBackendđẻ ra thêm một Lớp Cháu (VD:InternBackend), Lớp Cháu này sẽ "mù tịt", hoàn toàn không thấy bất kỳ tài sản nào của Lớp Ông Nội (NhanVien) nữa, vì tất cả đã bị thằng Bố (DevBackend) khóa mẹ vào két sắtprivatecủa riêng nó rồi!
Mẹo: Trong C++, nếu bạn viết class Con : Cha mà quên không ghi từ khóa gì, C++ sẽ tự động mặc định đó là kế thừa private. Cực kỳ nguy hiểm!
4. Bảng Cheat Sheet cho Kỹ sư C++
Để không bị "tẩu hỏa nhập ma" khi đi phỏng vấn hay lúc thiết kế hệ thống, anh em hãy lưu lại cái bảng tổng kết này:
| Quyền ở Lớp Cha | Kế thừa public | Kế thừa protected | Kế thừa private |
|---|---|---|---|
| private | ❌ Không truy cập được | ❌ Không truy cập được | ❌ Không truy cập được |
| protected | Giữ nguyên thành protected | Giữ nguyên thành protected | Bị hạ thành private |
| public | Giữ nguyên thành public | Bị hạ thành protected | Bị hạ thành private |
Tạm kết & Gợi mở
Tuyệt vời! Bây giờ anh em đã có thể tự tin đặt tay gõ từ khóa public khi kế thừa mà hiểu tận gốc rễ tại sao nó lại ở đó. Trong kiến trúc phần mềm, trừ khi bạn có một ý đồ đóng gói cực kỳ dị biệt, hãy luôn trung thành với Kế thừa public.
Xuyên suốt các bài qua, chúng ta liên tục sử dụng cấu trúc: 1 Cha truyền cho 1 Con (Single Inheritance - Kế thừa đơn). Nhưng thế giới thực phức tạp hơn nhiều. Có những Lớp Con được đẻ ra từ sự lai tạo của tận 2, 3 Lớp Cha khác nhau (Giống như Máy bán vé TVM vừa là thiết bị thanh toán, vừa là thiết bị in ấn). Hoặc một Lớp Ông Nội đẻ ra Cha, Cha đẻ ra Cháu, Cháu đẻ ra Chắt kéo dài cả chục đời.
C++ phân loại những cấu trúc đó như thế nào? Hẹn gặp lại anh em ở Bài 41: Kiểu kế thừa là gì? - Khám phá gia phả của các Object! Nhớ Upvote để mình có động lực ra bài mới nhé!
All rights reserved