友元函数与友元类
本课属于 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 的私有成员,但反过来不行。谨慎使用友元,因为它们会让类之间紧密耦合。友元最适合用于运算符重载和关系密切的辅助函数。
挑战
简单让我们构建一个安全消息系统,其中 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;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器