Menu
CoddyTech

Hàm và lớp bạn

Thuộc phần Lập trình hướng đối tượng trong hành trình C++ của Coddy. Bài học 32 trên 104.

Đôi khi, một hàm hoặc lớp bên ngoài lớp của bạn cần truy cập các thành viên riêng tư của lớp. Thay vì đặt các thành viên đó ở chế độ công khai, C++ cung cấp từ khóa friend để cấp quyền truy cập có chọn lọc mà vẫn duy trì tính đóng gói.

Hàm bạn được khai báo bên trong một lớp nhưng không phải là thành viên của lớp đó. Hàm này có thể truy cập các thành viên riêng tư và được bảo vệ của lớp:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // Khai báo printWidth là hàm bạn
    friend void printWidth(const Box& b);
};

// Định nghĩa hàm bạn (friend function), KHÔNG phải hàm thành viên
void printWidth(const Box& b) {
    std::cout << b.width;    // Có thể truy cập thành viên riêng tư
}

Lưu ý rằng printWidth được định nghĩa bên ngoài lớp mà không có toán tử phân giải phạm vi. Đây là một hàm thông thường nhưng có quyền truy cập đặc biệt.

Bạn cũng có thể cho phép toàn bộ một lớp làm bạn, để mọi hàm thành viên của lớp đó truy cập các thành viên riêng tư:

class Engine {
    int horsepower;
    
    friend class Mechanic;    // Mechanic có thể truy cập các thành viên riêng tư
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // Hợp lệ vì Mechanic là lớp bạn
    }
};

Tình bạn không có tính hai chiều. Nếu Engine khai báo Mechanic là bạn, Mechanic có thể truy cập các thành viên riêng tư của Engine, nhưng điều ngược lại thì không. Hãy dùng quan hệ bạn bè một cách hạn chế, vì chúng tạo ra sự liên kết chặt chẽ giữa các lớp. Chúng hữu ích nhất khi nạp chồng toán tử và viết các hàm trợ giúp có liên quan chặt chẽ.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống nhắn tin an toàn, trong đó lớp MessageReader cần quyền truy cập đặc biệt để đọc các tin nhắn đã mã hóa được lưu trong lớp SecureMessage. Đây là tình huống lý tưởng để dùng hàm bạn và lớp bạn.

Bạn sẽ tạo hai tệp để sắp xếp code:

  • SecureMessage.h: Định nghĩa lớp SecureMessage lưu dữ liệu tin nhắn ở chế độ riêng tư và cấp quyền truy cập có chọn lọc cho các thực thể đáng tin cậy. Lớp của bạn cần có:
    • Các thành viên riêng tư: content (chuỗi) và secretKey (số nguyên)
    • Một hàm khởi tạo nhận nội dung và khóa bí mật
    • Khai báo MessageReader là lớp bạn để lớp này có thể truy cập các thành viên riêng tư
    • Khai báo hàm bạn decryptMessage(const SecureMessage& msg, int key), hàm này trả về nội dung chỉ khi khóa được cung cấp khớp với khóa bí mật, nếu không thì trả về "Access Denied"

    Sau đó, định nghĩa lớp MessageReader trong cùng tệp tiêu đề:

    • Một phương thức readContent(const SecureMessage& msg) truy cập trực tiếp và trả về content riêng tư của tin nhắn
    • Một phương thức verifyKey(const SecureMessage& msg, int key) kiểm tra xem khóa được cung cấp có khớp với secretKey của tin nhắn hay không, rồi trả về "Valid" hoặc "Invalid"

    Cuối cùng, định nghĩa hàm bạn decryptMessage sau các lớp.

  • main.cpp: Minh họa cách dùng hàm bạn và lớp bạn. Đọc nội dung tin nhắn, khóa bí mật và khóa kiểm tra từ đầu vào, mỗi giá trị trên một dòng riêng, sau đó:
    • Tạo một đối tượng SecureMessage với nội dung và khóa bí mật
    • Tạo một đối tượng MessageReader
    • In ra "Reader access: <content>" bằng phương thức readContent() của đối tượng đọc
    • In ra "Key verification: <result>" bằng cách gọi verifyKey() với khóa kiểm tra
    • In ra "Decrypt with test key: <result>" bằng cách gọi hàm bạn với khóa kiểm tra
    • In ra "Decrypt with correct key: <result>" bằng cách gọi hàm bạn với khóa bí mật ban đầu

Lưu ý rằng hàm bạn được định nghĩa bên ngoài lớp, không có toán tử phân giải phạm vi: chúng là các hàm thông thường có quyền truy cập đặc biệt. Lớp MessageReader, với tư cách là lớp bạn, có thể truy cập trực tiếp các thành viên riêng tư của SecureMessage trong tất cả phương thức của nó.

Chuyển các đầu vào khóa từ chuỗi sang số nguyên bằng std::stoi(). Thêm tệp tiêu đề vào main.cpp bằng #include "SecureMessage.h".

Tự làm thử

#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;

int main() {
    // Đọc đầu vào
    string messageContent;
    string secretKeyStr;
    string testKeyStr;
    
    getline(cin, messageContent);
    getline(cin, secretKeyStr);
    getline(cin, testKeyStr);
    
    int secretKey = stoi(secretKeyStr);
    int testKey = stoi(testKeyStr);
    
    // TODO: Tạo SecureMessage với nội dung và khóa bí mật
    
    // TODO: Tạo đối tượng MessageReader
    
    // TODO: In ra "Reader access: <content>" bằng readContent()
    
    // TODO: In ra "Key verification: <result>" bằng verifyKey() với khóa kiểm tra
    
    // TODO: In ra "Decrypt with test key: <result>" bằng decryptMessage với khóa kiểm tra
    
    // TODO: In ra "Decrypt with correct key: <result>" bằng decryptMessage với khóa bí mật
    
    return 0;
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C++ online