如何运用结构型设计模式实现基础代理模式?

2026-05-07 18:421阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1257个文字,预计阅读时间需要6分钟。

如何运用结构型设计模式实现基础代理模式?

直接复制真实对象会导致代理破坏代理语法——你的代理是那个对象,而不是它的副本。当真实对象有状态或资源(如文件句柄、网络连接)时,赋值操作会导致状态或资源丢失。这时,值传递会导致行为错误或资源重复释放。

实操建议:

  • std::unique_ptr<RealSubject>RealSubject* 持有(推荐前者,明确所有权)
  • 若需共享生命周期,用 std::shared_ptr<RealSubject>,但要警惕循环引用
  • 避免裸指针 + 手动 new/delete —— 容易漏删、重复删,引发 double free 或悬空指针
  • 构造代理时传入已存在的真实对象实例,不要在代理内部自行构造(除非是虚拟代理场景)

虚函数表和继承关系决定能否拦截调用

代理模式依赖多态:用户通过基类接口操作代理,代理再转发给真实对象。如果 RealSubject 没有从公共抽象基类(比如 Subject)继承,或关键方法没声明为 virtual,代理就无法统一接口,也就谈不上“透明代理”。

常见错误现象:

立即学习“C++免费学习笔记(深入)”;

  • 编译报错 invalid static_castcannot convert from 'Proxy*' to 'Subject*'
  • 运行时调用的是代理自己的实现,而非真实对象的方法(因未覆写虚函数)
  • 子类新增方法无法被代理识别(违反里氏替换)

实操建议:

  • 定义纯虚基类 Subject,所有方法声明为 virtual,至少含一个 virtual ~Subject() = default;
  • ProxyRealSubject 都公有继承 Subject
  • 代理中覆写每个虚函数,显式调用 m_real->method(),别漏掉 const 重载版本

代理对象的生命周期不能早于真实对象结束

这是最隐蔽也最致命的问题:一旦真实对象析构了,代理还拿着它的指针/引用,后续任何转发调用都变成未定义行为(UB),轻则 crash,重则静默数据损坏。

使用场景中容易踩坑的情况:

  • 真实对象是栈变量,代理在函数外继续使用(RealSubject r; Proxy p{&r}; return p;
  • 真实对象由局部 std::shared_ptr 管理,但代理持有了裸指针
  • 多线程环境下,真实对象被其他线程提前释放,代理无感知

实操建议:

  • 强制代理与真实对象共用智能指针:把 std::shared_ptr<RealSubject> 传给代理构造函数,并存为成员
  • 若必须用裸指针,加注释说明“caller must ensure lifetime”,并在 debug 模式下用 assert(m_real != nullptr) 做基础防护
  • 避免在工厂函数中返回栈上代理对象绑定栈上真实对象的组合

std::shared_ptr 的线程安全边界要划清

很多人以为用了 std::shared_ptr 就天然线程安全——其实只保证引用计数原子性,不保证它指向的对象线程安全。代理转发调用时,若多个线程同时通过不同代理实例操作同一个 RealSubject,仍可能引发竞态。

性能与兼容性影响:

  • 频繁拷贝 std::shared_ptr 有轻微开销(原子增减引用计数),高频调用场景可考虑 std::weak_ptr 配合锁
  • Windows MSVC 2015+ 和 GCC 5.0+ 对 std::shared_ptr 的线程安全行为一致,但老编译器需查文档确认
  • 若真实对象本身带锁(如 std::mutex 成员),代理层不应再套一层锁,否则易死锁

实操建议:

  • 代理转发前不做额外同步;同步责任落在 RealSubject 实现内部
  • 需要读写分离时,用 std::shared_ptr<const RealSubject> 表达只读语义
  • 调试时开启 -D_GLIBCXX_DEBUG(GCC)或 _ITERATOR_DEBUG_LEVEL=2(MSVC)捕获悬空访问

代理模式真正难的从来不是写那几行转发代码,而是厘清谁拥有对象、谁负责销毁、谁来保证并发安全——这些边界一旦模糊,问题往往在压测或上线后才爆发,且难以复现。

标签:C

本文共计1257个文字,预计阅读时间需要6分钟。

如何运用结构型设计模式实现基础代理模式?

直接复制真实对象会导致代理破坏代理语法——你的代理是那个对象,而不是它的副本。当真实对象有状态或资源(如文件句柄、网络连接)时,赋值操作会导致状态或资源丢失。这时,值传递会导致行为错误或资源重复释放。

实操建议:

  • std::unique_ptr<RealSubject>RealSubject* 持有(推荐前者,明确所有权)
  • 若需共享生命周期,用 std::shared_ptr<RealSubject>,但要警惕循环引用
  • 避免裸指针 + 手动 new/delete —— 容易漏删、重复删,引发 double free 或悬空指针
  • 构造代理时传入已存在的真实对象实例,不要在代理内部自行构造(除非是虚拟代理场景)

虚函数表和继承关系决定能否拦截调用

代理模式依赖多态:用户通过基类接口操作代理,代理再转发给真实对象。如果 RealSubject 没有从公共抽象基类(比如 Subject)继承,或关键方法没声明为 virtual,代理就无法统一接口,也就谈不上“透明代理”。

常见错误现象:

立即学习“C++免费学习笔记(深入)”;

  • 编译报错 invalid static_castcannot convert from 'Proxy*' to 'Subject*'
  • 运行时调用的是代理自己的实现,而非真实对象的方法(因未覆写虚函数)
  • 子类新增方法无法被代理识别(违反里氏替换)

实操建议:

  • 定义纯虚基类 Subject,所有方法声明为 virtual,至少含一个 virtual ~Subject() = default;
  • ProxyRealSubject 都公有继承 Subject
  • 代理中覆写每个虚函数,显式调用 m_real->method(),别漏掉 const 重载版本

代理对象的生命周期不能早于真实对象结束

这是最隐蔽也最致命的问题:一旦真实对象析构了,代理还拿着它的指针/引用,后续任何转发调用都变成未定义行为(UB),轻则 crash,重则静默数据损坏。

使用场景中容易踩坑的情况:

  • 真实对象是栈变量,代理在函数外继续使用(RealSubject r; Proxy p{&r}; return p;
  • 真实对象由局部 std::shared_ptr 管理,但代理持有了裸指针
  • 多线程环境下,真实对象被其他线程提前释放,代理无感知

实操建议:

  • 强制代理与真实对象共用智能指针:把 std::shared_ptr<RealSubject> 传给代理构造函数,并存为成员
  • 若必须用裸指针,加注释说明“caller must ensure lifetime”,并在 debug 模式下用 assert(m_real != nullptr) 做基础防护
  • 避免在工厂函数中返回栈上代理对象绑定栈上真实对象的组合

std::shared_ptr 的线程安全边界要划清

很多人以为用了 std::shared_ptr 就天然线程安全——其实只保证引用计数原子性,不保证它指向的对象线程安全。代理转发调用时,若多个线程同时通过不同代理实例操作同一个 RealSubject,仍可能引发竞态。

性能与兼容性影响:

  • 频繁拷贝 std::shared_ptr 有轻微开销(原子增减引用计数),高频调用场景可考虑 std::weak_ptr 配合锁
  • Windows MSVC 2015+ 和 GCC 5.0+ 对 std::shared_ptr 的线程安全行为一致,但老编译器需查文档确认
  • 若真实对象本身带锁(如 std::mutex 成员),代理层不应再套一层锁,否则易死锁

实操建议:

  • 代理转发前不做额外同步;同步责任落在 RealSubject 实现内部
  • 需要读写分离时,用 std::shared_ptr<const RealSubject> 表达只读语义
  • 调试时开启 -D_GLIBCXX_DEBUG(GCC)或 _ITERATOR_DEBUG_LEVEL=2(MSVC)捕获悬空访问

代理模式真正难的从来不是写那几行转发代码,而是厘清谁拥有对象、谁负责销毁、谁来保证并发安全——这些边界一旦模糊,问题往往在压测或上线后才爆发,且难以复现。

标签:C