
代理模式在 C++ 中不是靠语言特性强制实现的,而是通过类设计达成的——核心是让代理类持有真实对象的引用或指针,并在转发调用前后插入控制逻辑。
代理类必须继承同一接口(或使用模板约束)
否则无法实现“对客户端透明”的替换。最常见做法是定义抽象基类 IFileReader,让 RealFileReader 和 LoggingFileReader 都继承它:
class IFileReader {
public:
virtual ~IFileReader() = default;
virtual std::string read(const std::string& path) = 0;
};
代理类不直接暴露真实对象类型,客户端只依赖 IFileReader 接口。若用模板实现静态代理(如 Proxy),则需确保 T 具备一致的公有接口,且代理要重载所有需拦截的函数。
访问控制逻辑应放在代理的成员函数内部,而非构造时硬编码
比如权限检查不能只在构造函数里做一次,而应在每次 read() 调用前验证。典型错误是把用户 ID 存为代理类的 const 成员,导致后续请求无法动态鉴权:
立即学习“C++免费学习笔记(深入)”;
- ✅ 正确:每次调用
read()前查ACLManager::canRead(user_id, path) - ❌ 错误:构造时传入
user_id并存为const,后续绕过检查直接转发 - 注意:若控制逻辑含状态(如调用计数、缓存),需明确生命周期——是 per-call、per-proxy 还是全局共享
避免裸指针引发的悬挂与内存泄漏
代理类持有真实对象时,优先用 std::shared_ptr 或 std::unique_ptr,而不是 IFileReader*:
-
std::shared_ptr适合多代理共享同一真实对象(如连接池场景) -
std::unique_ptr更轻量,适合代理完全拥有真实对象(如资源封装) - 裸指针 + 手动
new/delete极易出错:真实对象提前析构 → 代理调用时崩溃;代理析构未释放 → 内存泄漏
尤其当代理作为函数参数传递或存储在容器中时,智能指针能自动管理所有权边界。
性能敏感场景慎用虚函数+动态分发
纯虚函数调用有间接跳转开销,在高频调用路径(如每帧渲染中的资源访问)可能成为瓶颈。可考虑:
- 用模板 + CRTP 实现静态代理:
Proxy在编译期绑定,零运行时开销 - 将部分控制逻辑(如日志开关)设为编译期常量(
if constexpr (EnableLog)),避免分支预测失败 - 若仅需简单访问控制(如只读/可写标记),可改用组合 + 成员函数返回值校验,绕过继承体系
代理不是银弹——它增加了一层间接性。真正需要拦截行为时才加代理,别为了“架构漂亮”而提前抽象。










