
设想你是C++语言编程以及软件工程实践专家,一起聊聊C++开发的一些话题,最近在阅读More C++ idioms wikibook,里面有很多不错的关于C++开发应用的开发话题
ChatGPT 说: 《More C++ Idioms》是一本非常经典的 C++ 进阶书籍,深入探讨了 C++ 语言中的惯用法(idioms)。这些 idioms 体现了 C++ 的强大抽象能力,许多现代 C++ 代码库都可以看到它们的影子。
我们可以聊聊书中涉及的 C++ 设计模式、惯用法,或者它们在实际开发中的应用。你对哪些部分感兴趣?比如:
Curiously Recurring Template Pattern (CRTP)——用来实现静态多态、编译期优化等。 Type Erasure——用于实现泛型编程,比如 std::function 的底层原理。 Policy-based Design——像 std::allocator 这样的策略模式如何影响 STL 设计。 Small Object Optimization (SOO)——像 std::string 那样的小对象优化策略。 Non-Copyable 和 Empty Base Optimization (EBO)——提高 C++ 类的效率。 你最近对哪些 C++ 话题最感兴趣,或者在实际工程中遇到了什么有趣的 C++ 设计问题?
您说: 我们一个个惯用法来聊吧, 先从这个惯用法: Friendship and the Attorney-Client
ChatGPT 说: Friendship and the Attorney-Client Idiom(友元与委托模式) 概念 这个惯用法利用 C++ friend 关键字,使一个“Client”类只能通过一个“Attorney”类来访问某个“Server”类的私有或受保护成员。
它的核心思想是:
- “Server”(服务器)类将某些权限授予一个“Attorney”类,而不直接暴露给所有客户端。
- “Attorney”(律师)类有访问 Server 私有/受保护成员的权限,并在受控范围内提供服务。
- “Client”(客户)只能通过“Attorney”来间接访问 Server。
这个惯用法的主要作用是精细化控制访问权限,在不破坏封装的情况下,实现更严格的访问管理。
#include <iostream>
// Server 类:只希望 Attorney 能访问其私有成员
class Server {
private:
void sensitiveOperation() {
std::cout << "Sensitive operation performed\n";
}
public:
// 让 Attorney 作为 friend,避免所有人直接访问 sensitiveOperation
friend class Attorney;
};
// Attorney 类:受控地暴露 Server 的功能
class Attorney {
public:
static void allowAccess(Server& server) {
server.sensitiveOperation(); // 访问私有成员
}
};
// Client 类:不能直接访问 Server,只能通过 Attorney 访问
class Client {
public:
void doWork() {
Server server;
Attorney::allowAccess(server); // 只能通过 Attorney 调用
}
};
int main() {
Client client;
client.doWork();
return 0;
}
应用场景 精细化权限控制
- 只让特定的“律师类”访问核心功能,而不直接暴露给所有用户。 例如,一个数据库类 Database 只允许 TransactionManager 提交事务,而不是所有代码都能调用 commit()。
- 隐藏实现细节
让 Server 类的私有实现细节对普通用户不可见,增强封装性。 例如,std::allocator 可能使用类似的模式,让 std::vector 访问其内部 API,但不让普通用户直接访问。 - 模拟访问控制系统
在某些安全关键系统中,需要通过 Attorney 来决定 Client 是否有权限调用 Server 的某些功能。 - 对比传统 friend 的使用 很多人认为 friend 破坏了封装,但 Attorney-Client 方式是一个更好的封装方式:
传统 friend: - 直接把 friend 赋给 Client,但这样 Client 可能滥用权限。 - Attorney-Client Idiom: - 通过 Attorney 作为受控的访问通道,精确管理 Client 能执行哪些操作。 改进版本:模板实现 如果有多个 Server 类都需要类似的 Attorney 访问,可以用 模板 进行泛化:
template <typename ServerType>
class Attorney {
public:
static void allowAccess(ServerType& server) {
server.sensitiveOperation();
}
};
class Server {
private:
void sensitiveOperation() {
std::cout << "Sensitive operation performed\n";
}
friend class Attorney<Server>; // 让特定 Attorney 访问
};
int main() {
Server server;
Attorney<Server>::allowAccess(server);
}
这样 Attorney 变成了一个通用的访问管理器,可以适配不同的 Server 类型。
总结 ✅ 优点:
- 让 friend 更有针对性,避免破坏封装。
- 允许精细化权限管理,减少暴露私有 API 的风险。
易于扩展,比如结合模板进行通用化设计。 ❌ 缺点:
增加了一层间接访问,可能会影响代码可读性。
- 过度使用 friend 仍然可能导致难以维护的代码。
- 适用场景: 适用于需要 严格权限控制、但又不想暴露内部实现的 C++ 设计,例如数据库管理器、事务控制器等。
下一步 如果你对这个惯用法的实际应用或优化有兴趣,我们可以继续探讨,比如:
- 结合 std::unique_ptr 等智能指针管理访问权限
- 在多继承结构下的使用方法
- 现代 C++(C++11 及以后)如何改进这种模式 你觉得这个惯用法在你的 C++ 开发中是否有应用价值?🚀
否有应用价值?🚀
您说: 基本理解了这个概念,有两个关键点:1 使用friend这个语言特性,2 使用增加中间层类设计,透过中间类精细化控制
ChatGPT 说: 是的,你总结得很准确!Friendship and the Attorney-Client Idiom 主要基于两个核心设计点:
1️⃣ 使用 friend 语言特性 —— 让 Server 只允许 Attorney 访问其私有成员,而不直接对 Client 开放。 2️⃣ 增加中间层 Attorney —— 作为受控访问通道,确保 Client 只能通过 Attorney 访问 Server 的私有功能,从而实现更精细的访问控制。
进一步探讨
如果我们希望更灵活地管理访问权限,还可以引入策略模式(Policy-based Design),让不同 Attorney 类控制不同的权限。
例如:
#include <iostream>
// Server 类
class Server {
private:
void sensitiveRead() { std::cout << "Reading sensitive data\n"; }
void sensitiveWrite() { std::cout << "Writing sensitive data\n"; }
// 让不同 Attorney 访问不同权限
friend class ReadAttorney;
friend class WriteAttorney;
};
// Attorney: 只允许读
class ReadAttorney {
public:
static void read(Server& server) {
server.sensitiveRead();
}
};
// Attorney: 只允许写
class WriteAttorney {
public:
static void write(Server& server) {
server.sensitiveWrite();
}
};
// Client
int main() {
Server server;
ReadAttorney::read(server); // 只能读
WriteAttorney::write(server); // 只能写
}
拓展思考 这样,我们可以创建多个 Attorney,分别管理不同的权限,而不是让一个 Attorney 访问所有功能。 这在权限管理、资源访问控制等场景中非常有用,比如: - FileReadAttorney 只允许读取文件; - FileWriteAttorney 只允许写入文件; - DatabaseTransactionAttorney 只允许提交事务; 甚至可以结合模板,将 Attorney 设计成可配置策略! 如果你感兴趣,我们可以继续深入探讨如何在现代 C++(C++11 及以后)中优化这种设计,比如结合 std::function 或 std::unique_ptr 来管理访问权限。🚀
暂且打住,不同模式先不用混杂在一起,为了应对更复杂得应用场景,多个设计模式组合在一起使用是常态,得逐渐依赖经验得积累
下一篇我们再讨论一下Policy-based desay模式

评论