代理类必须与被代理类实现同一接口,所有函数需完整转发并插入控制逻辑;返回值类型须严格一致;权限控制应降级处理而非抛异常;operator->和operator*需正确重载;std::shared_ptr持有目标时应声明为const。

代理类怎么写才真正拦截访问
代理模式的核心不是“套一层类”,而是让代理对象在用户无感知时接管对目标对象的操作。关键在于:代理类必须和被代理类实现同一接口,且所有对外暴露的函数都得转发——但不是简单调用,得在转发前后插入控制逻辑。
常见错误是只代理了部分函数,比如只重写了 get() 却漏掉 set() 或 save(),结果访问控制形同虚设。更隐蔽的坑是返回值类型没处理好:如果接口返回 std::shared_ptr<Data>,代理返回裸指针或值拷贝,就绕过了代理层。
- 接口定义必须用纯虚函数,强制派生类实现;不要用模板接口混用,容易导致 Slicing 或静态绑定失效
- 代理类构造时建议用
std::shared_ptr持有目标对象,避免生命周期错位;裸指针 +new容易引发 double-free - 若需延迟初始化(比如连接数据库前不创建真实对象),在代理的每个函数里加惰性构造逻辑,而不是在构造函数里硬创建
如何控制读/写/执行权限而不破坏接口契约
权限控制不能靠抛异常来“拦住”调用,而要在语义层面做降级或过滤。比如用户无读权限时,getData() 不该崩溃,而应返回空值或默认值;无写权限时,setData() 应静默忽略或记录审计日志。
硬编码权限检查会污染业务逻辑。推荐把权限判断抽成策略对象,比如传入 std::function<bool(const std::string&)> 类型的检查器,参数是操作名("read"、"write"),这样后续换鉴权方式(RBAC / ABAC)不用改代理类。
立即学习“C++免费学习笔记(深入)”;
- 别在代理里写
if (user.role == "admin")这种硬逻辑;角色信息应由外部注入,代理只负责触发检查 - 涉及多线程时,权限检查本身可能需要锁,但锁粒度要小——只锁检查过程,别把整个
getData()包进临界区 - 如果被代理对象本身有缓存(如
cache_成员),代理层也要同步维护或失效它,否则权限变化后旧缓存仍可访问
为什么 operator-> 和 operator* 代理容易出错
智能指针式代理(比如模仿 std::unique_ptr 行为)必须重载 operator-> 和 operator*,否则用户写 proxy->func() 会直接调到代理类自己的成员,而不是转发给目标对象。
典型错误是只重载了 operator->,忘了 operator*,结果 (*proxy).func() 编译失败;或者两个操作符都返回 this,造成无限递归。
-
operator->()必须返回原始指针或另一个重载了operator->的对象,不能返回代理自身 -
operator*()应返回目标对象的引用(T&),不是代理自身的引用;返回*this是常见误写 - 如果目标对象是 const-sensitive(比如有 const/non-const 版本的
data()),代理也得提供对应 const 重载,否则const Proxy p; p->data();会编译不过
std::shared_ptr<T> 做代理容器时的隐含陷阱
用 std::shared_ptr 包裹目标对象看似方便,但会掩盖所有权语义。比如你本想代理一个栈上对象,却误用 std::shared_ptr<T>(new T) 创建堆对象,不仅内存泄漏风险高,还让“代理只是访问控制层”的意图彻底丢失。
更麻烦的是,如果用户把代理的 std::shared_ptr 拿去 reset() 或重新赋值,目标对象可能提前析构,而代理对象还在继续转发调用,结果就是未定义行为。
- 代理类内部的
std::shared_ptr应声明为const(即const std::shared_ptr<T> target_;),禁止外部修改其指向 - 若目标对象生命周期不由代理管理(比如是全局单例或栈对象),改用
std::reference_wrapper<T>或裸指针 + 生命周期注释,比强行套shared_ptr更诚实 - 调试时注意:gdb 显示
proxy.target_.get()是空,不代表目标不存在——可能只是 weak_ptr 已过期,得检查是否用了lock()而非直接解引用
代理最难的从来不是语法,而是厘清“谁拥有、谁控制、谁负责销毁”。接口一致、转发干净、权限透明,这三件事没做扎实,后面加多少日志和监控都补不回语义漏洞。



















