[C++ OOP Thực Chiến] Bài 23: Lớp bạn (Friend Class) - Khi một người bạn được giao "chìa khóa nhà"
Chào anh em! Ở [Bài 22], chúng ta đã biết cách "mở cửa hậu" cho một hàm tự do bằng từ khóa friend.
Nhưng hãy tưởng tượng, bạn đang viết một hệ thống tính toán khoa học. Bạn có một Class ToanHoc chứa hàng chục phương thức phức tạp: cong(), tru(), nhan(), chia(), daoHam(), tichPhan()... và TẤT CẢ các hàm này đều cần đọc dữ liệu private (tử số, mẫu số) của Class PhanSo.
Thay vì mở file PhanSo.h ra và copy-paste 20 dòng friend cho 20 cái hàm đó, C++ cho phép bạn cấp quyền "bạn thân" cho TOÀN BỘ Class ToanHoc chỉ bằng ĐÚNG 1 DÒNG CODE.
1. Lớp bạn (Friend Class) là gì?
Nếu Hàm bạn là việc bạn cho phép một người khách bước vào phòng ngủ mở két sắt, thì Lớp bạn giống như việc bạn giao hẳn chùm chìa khóa nhà cho một gia đình khác.
Khi Class A tuyên bố Class B là "Lớp bạn" của nó:
- Toàn bộ các phương thức bên trong Class
Bđều có quyền truy cập trực tiếp vào các thành viênprivatevàprotectedcủa ClassA. - Mã nguồn của Class
Atrông sẽ cực kỳ gọn gàng.
2. Ba "Quy tắc thép" về Tình bạn trong C++
Khi dùng Friend Class trong các dự án Backend/Hệ thống, anh em tuyệt đối phải nhớ 3 quy tắc sau (rất hay bị hỏi khi đi phỏng vấn):
- Tình bạn chỉ đến từ một phía (One-Way): Class
PhanSocoiToanHoclà bạn (cho phépToanHocxem dữ liệu của mình). Điều đó KHÔNG CÓ NGHĨA làPhanSođược phép xem dữ liệuprivatecủaToanHoc. Tình bạn trong OOP rất phũ phàng! - Tình bạn không có tính chất bắc cầu: Nếu
Alà bạn củaB,Blà bạn củaC, thìAKHÔNG PHẢI là bạn củaC. Mỗi quyền truy cập đều phải được khai báo rõ ràng. - Phá vỡ tính Đóng gói (Encapsulation): Giao chìa khóa nhà cho quá nhiều người thì nhà bạn không còn an toàn nữa. Chỉ dùng Friend Class cho những Class thực sự có mối liên hệ mật thiết (ví dụ: Class
Databasevà ClassQueryBuilder).
3. Code Demo: Giao quyền cho Class Toán Học
Hãy xem cách chúng ta thiết lập mối quan hệ này trong C++:
#include <iostream>
using namespace std;
class PhanSo {
private:
int tuSo;
int mauSo;
public:
PhanSo(int tu, int mau) : tuSo(tu), mauSo(mau) {}
void inPhanSo() const {
cout << tuSo << "/" << mauSo << "\n";
}
// 1 DÒNG DUY NHẤT: Cấp quyền cho toàn bộ Class ToanHoc
friend class ToanHoc;
};
// --- CLASS TOÁN HỌC ---
class ToanHoc {
public:
// Vì ToanHoc là BẠN, nó có thể chọc thẳng vào tuSo, mauSo của PhanSo
static PhanSo cong(const PhanSo& a, const PhanSo& b) {
int tu = a.tuSo * b.mauSo + b.tuSo * a.mauSo;
int mau = a.mauSo * b.mauSo;
return PhanSo(tu, mau);
}
static PhanSo nhan(const PhanSo& a, const PhanSo& b) {
// Truy cập private cực kỳ thoải mái
return PhanSo(a.tuSo * b.tuSo, a.mauSo * b.mauSo);
}
};
int main() {
cout << "--- HE THONG TOAN HOC ---\n";
PhanSo ps1(1, 2); // 1/2
PhanSo ps2(3, 4); // 3/4
cout << "PS1: "; ps1.inPhanSo();
cout << "PS2: "; ps2.inPhanSo();
// Sử dụng Class ToanHoc để xử lý (Gọi hàm tĩnh)
PhanSo tong = ToanHoc::cong(ps1, ps2);
PhanSo tich = ToanHoc::nhan(ps1, ps2);
cout << "\nTong: "; tong.inPhanSo();
cout << "Tich: "; tich.inPhanSo();
return 0;
}
Nhận xét: Bằng cách tách biệt Class lưu trữ dữ liệu (PhanSo) và Class xử lý logic (ToanHoc), hệ thống của bạn tuân thủ rất tốt nguyên tắc Single Responsibility (Đơn nhiệm) trong thiết kế phần mềm, đồng thời code vẫn vô cùng tối ưu do không phải đi qua các hàm Getter cồng kềnh.
Tạm kết & Gợi mở
Vậy là chúng ta đã nắm trọn vẹn nghệ thuật thao túng quyền truy cập trong OOP với friend (cả Function lẫn Class).
Tuy nhiên, hãy nhìn vào hàm main() ở code demo trên. Để cộng hai phân số, chúng ta phải viết:
PhanSo tong = ToanHoc::cong(ps1, ps2);
Code chạy đúng, nhưng nhìn nó rất... "phèn" và cồng kềnh!
Đối với những kiểu dữ liệu có sẵn như int hay float, C++ cho phép bạn viết cực kỳ thanh lịch: int tong = a + b;.
Tại sao chúng ta không thể viết PhanSo tong = ps1 + ps2;? Tại sao C++ không cho phép chúng ta dùng thẳng dấu cộng + cho cái Class do chính chúng ta tạo ra?
Tin vui là C++ HOÀN TOÀN CHO PHÉP điều đó! Nó gọi là phép thuật thao túng toán tử. Hẹn gặp lại anh em ở Bài 24: Giới thiệu nạp chồng toán tử (Operator Overloading) - Phép thuật định hình đẳng cấp C++! Nhớ Upvote để ủng hộ mình ra bài mới nhé!
All Rights Reserved