Menu
CoddyTech

友元函数与友元类

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 32 课,共 104 课。

有时,你的类之外的函数或类需要访问它的私有成员。C++ 提供了 friend 关键字,可以授予选择性访问权限,同时保持封装性,而不必将这些成员设为公有。

友元函数在类内部声明,但不是该类的成员。它可以访问类的私有成员和受保护成员:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // 将 printWidth 声明为友元函数
    friend void printWidth(const Box& b);
};

// 友元函数的定义,不是成员函数
void printWidth(const Box& b) {
    std::cout << b.width;    // 可以访问私有成员
}

注意,printWidth 是在类外定义的,没有使用作用域解析运算符。它是一个普通函数,只是恰好拥有特殊访问权限。

你也可以将整个类设为友元类,让它的所有成员函数都能访问私有成员:

class Engine {
    int horsepower;
    
    friend class Mechanic;    // Mechanic 可以访问私有成员
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // 允许访问:Mechanic 是友元类
    }
};

友元关系不是相互的。如果 Engine 将 Mechanic 声明为友元,Mechanic 就可以访问 Engine 的私有成员,但反过来不行。谨慎使用友元,因为它们会让类之间紧密耦合。友元最适合用于运算符重载和关系密切的辅助函数。

challenge icon

挑战

简单

让我们构建一个安全消息系统,其中 MessageReader 类需要特殊权限,以读取存储在 SecureMessage 类中的加密消息。这是使用友元函数和友元类的理想场景。

你将创建两个文件来组织代码:

  • SecureMessage.h:定义一个 SecureMessage 类,用于存储私有消息数据,并向受信任的实体授予选择性访问权限。你的类应包含:
    • 私有成员:content(字符串)和 secretKey(整数)
    • 一个接收消息内容和密钥的构造函数
    • 将 MessageReader 声明为友元类,使其能够访问私有成员
    • 声明一个友元函数 decryptMessage(const SecureMessage& msg, int key),仅当提供的密钥与密钥匹配时才返回消息内容,否则返回 "Access Denied"

    然后在同一个头文件中定义 MessageReader 类:

    • 一个方法 readContent(const SecureMessage& msg),直接访问并返回消息的私有成员 content
    • 一个方法 verifyKey(const SecureMessage& msg, int key),检查提供的密钥是否与消息的 secretKey 匹配,并返回 "Valid" 或 "Invalid"

    最后,在类的定义之后定义友元函数 decryptMessage。

  • main.cpp:演示友元函数和友元类的实际用法。从输入中读取消息内容、密钥和测试密钥(每项各占一行),然后:
    • 使用消息内容和密钥创建一个 SecureMessage
    • 创建一个 MessageReader 对象
    • 使用读取器的 readContent() 方法打印 "Reader access: <content>"
    • 使用测试密钥调用 verifyKey(),打印 "Key verification: <result>"
    • 使用测试密钥调用友元函数,打印 "Decrypt with test key: <result>"
    • 使用原始密钥调用友元函数,打印 "Decrypt with correct key: <result>"

请记住,友元函数定义在类外,不使用作用域解析运算符:它们是拥有特殊访问权限的普通函数。作为友元的 MessageReader 类可以在它的所有方法中直接访问 SecureMessage 的私有成员。

使用 std::stoi() 将输入的密钥字符串转换为整数。在 main.cpp 中使用 #include "SecureMessage.h" 引入头文件。

亲手试试

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

int main() {
    // 读取输入
    string messageContent;
    string secretKeyStr;
    string testKeyStr;
    
    getline(cin, messageContent);
    getline(cin, secretKeyStr);
    getline(cin, testKeyStr);
    
    int secretKey = stoi(secretKeyStr);
    int testKey = stoi(testKeyStr);
    
    // TODO: 用内容和密钥创建一个 SecureMessage 对象
    
    // TODO: 创建一个 MessageReader 对象
    
    // TODO: 使用 readContent() 输出 "Reader access: <content>"
    
    // TODO: 使用 verifyKey() 和测试密钥输出 "Key verification: <result>"
    
    // TODO: 使用 decryptMessage 和测试密钥输出 "Decrypt with test key: <result>"
    
    // TODO: 使用 decryptMessage 和正确的密钥输出 "Decrypt with correct key: <result>"
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器