当前位置: 首页 > news >正文

智能指针之设计模式4

前面的文章介绍了使用工厂模式来封装智能指针对象的创建过程,下面介绍一下工厂类

enable_shared_from_this的实现方案。

4、模板方法模式

在前面的文章分析过,enable_shared_from_this<T>类是一个工厂基类,提供的工厂方法是shared_from_this(),而shared_ptr<T>就是一个具体的产品类,每一个shared_ptr<T>对象都需要一个具体的工厂类来创建它,这个具体的工厂类就是T,它是enable_shared_from_this<T>的派生类,同时又是模板参数类型。那么,C++标准库是怎么通过this指针来创建shared_ptr<T>对象的呢?

按照常规的做法,可以继承工厂基类然后由派生类来实现基类定义的工厂方法接口,然而当在一个对象内部通过this指针创建一个shared_ptr对象时,却不是一件容易的事情:

首先,要判断资源对象是否是在堆中,如果资源对象不是在堆中,那么使用shared_ptr<T>(this)创建的智能指针对象,在析构时就会发生异常。

其次,要确定资源对象是否已经被shared_ptr管理了,如果管理了,也不能直接使用shared_ptr<T>(this)创建的智能指针对象,否则会导致由两个不同的shared_ptr来管理this,最后会发生重复释放内存的错误。

再次,如果在资源对象内部使用shared_ptr<T>(this)创建了智能指针对象,但是用户在外部是不知道的,可能又使用shared_ptr<T>(obj_ptr)创建了智能指针对象,也会发生重复释放内存的错误。

如果这个创建过程,完全交给程序员来实现,需要仔细地约束各方面的代码实现,还得需要相关人员认真地评审,防止出现上面描述的错误,带来了极大的风险和不便。C++标准库的思路是交由代码来解决,把通过this指针创建shared_ptr对象的代码封装起来形成固定的模板套路,只把需要定制的部分交给派生类去实现,也就是模板方法模式,即在模板基类中把通过this指针创建shared_ptr对象的过程封装成“模板方法”,它执行过程中要使用具体派生类提供的“钩子函数",这样哪个类要想提供shared_from_this()功能,就继承模板基类并实现”钩子函数“就可以了。下面是GOF模板方法模式的结构图:
在这里插入图片描述

从结构图中可以看出,模板方法模式实际上就是面向对象编程中的继承和动态多态机制,也就是在基类中定义virtual函数接口,也就是“钩子方法”,在派生类中实现virtual函数,从而让基类中的“模板方法”能够通过动态绑定的方式,来调用派生类提供的virtual函数。

不过enable_shared_from_this<T>作为模板基类与上面的模板方法模式在形式上不同,它没有采用面向对象编程的多态机制,而是利使用了一种特殊的继承方式实现的,即CRTP。CRTP采用了C++的模板技术,虽然也是继承方式,但却是在基类中通过把自己强制转为派生类类型的方式来调用派生类实现的函数,这是二者最大的区别。模板方法模式是一个共有的基类,然后有多个同一族类的派生类,它们复用了基类中的“模板方法”,并重写了virtual函数,属于动态绑定;而CRTP方式是一个基类仅有一个派生类,这样完全可以把基类强制转换成派生类,调用的是派生类的非virtual成员函数,属于静态绑定机制。同样,因为CRTP派生类继承了基类,它复用了基类的“模板方法”,因此,外界也可以调用派生类的“模板方法”来实现具体的功能。

shared_from_this()创建shared_ptr对象的套路是,定义了一个weak_ptr数据成员,在创建shared_ptr对象时,就使用这个weak_ptr成员来创建,派生类显然是无法设置这个weak_ptr成员的,也就是说无法设计成提供一个“钩子函数”的形式,而weak_ptr类型和shared_ptr类型息息相关,因此enable_shared_from_this<T>就让shared_ptr<T>作为它的友元类,要求shared_ptr在创建对象时,同时初始化这个weak_ptr成员。这样,如果派生类T没有使用shared_ptr对象来管理自己的生存期,也就初始化不了这个weak_ptr成员,进而也就无法通过this创建出shared_ptr对象,而通过weak_ptr对象创建的shared_ptr对象和原来的shared_ptr对象共享T资源对象的所有权,也不会发生直接使用this指针创建了shared_ptr对象之后,导致由两个不同的shared_ptr来管理this的错误。

可见,在这个模板方法模式中,“钩子方法”不是一个具体函数,它是一个抽象的概念,或者说是一个“钩子数据”,在这里就是它的weak_ptr类型的数据成员,模板方法在实现套路化的逻辑时,需要使用weak_ptr对象数据成员来创建shared_ptr对象,这个weak_ptr对象是在创建shared_ptr对象时初始化的,因此要求创建它的派生类对象时,必须要被一个shared_ptr对象托管,这样shared_ptr在创建对象时,就会同时初始化这个weak_ptr成员。也就是说,派生类不需要提供具体的“钩子函数”,只要它在创建时候,同时创建一个shared_ptr对象来管理它的生存期,它自然而然地就为基类的“模板方法”提供了“钩子数据”。

这里,相当于把通过this指针创建shared_ptr对象的过程给封装成模板方法,谁要是想通过this指针创建shared_ptr对象就可以直接继承enable_shared_from_this类,它的模板方法自己实现了这个功能,派生类无需专门编写代码,为程序员编程带来了便利。下面是结构图:
在这里插入图片描述

enable_shared_from_this<resource>是工厂基类也是模板方法类,它的成员函数shared_from_this()是"模板方法",职责是使用资源对象resource的this指针来创建一个shared_ptr对象;资源对象resource类是它的一个公共派生类,是工厂派生类也是模板方法派生类,具体产品对象shared_ptr<resource>就是从resource中创建出来的,也就是一个产品对象通过“模板方法”创建了一个管理自己生存期的shared_ptr对象。示例代码如下:

class resource : public enable_shared_from_this<resource> {data_obj obj;public:void process() {auto sp = shared_from_this();...}... // 其它成员函数
};
...

只要继承了enable_shared_from_this<T>类,它就是一个模板方法派生类,就可以使用模板方法类的shared_from_this(),然而这只是“模板方法”函数,还得需要派生类自己提供“钩子方法”函数,也就是保证派生类创建的对象被shared_ptr托管,这样就可以根据模板方法来通过this指针创建shared_ptr的副本了。因此,resoure资源对象可以通过:

shared_ptr<resource> res1(new resource);
shared_ptr<resource> res2 = make_shared<resource>();
shared_ptr<resource> res3 = weak_ptr_obj.lock();
shared_ptr<resource> res4(weak_ptr_obj());
shared_ptr<resource> res5(unique_ptr_obj());

等这些方式来创建shared_ptr<resource>对象,可以把这些创建方式等同于“钩子方法”,派生类resource只要能够创建出shared_ptr<resource>对象,当在resource对象内部使用this指针调用“模板方法”shared_shared_this()时,就会按照固定的套路创建出一个shared_ptr<resource>对象。显然这样就简化了根据this创建shared_ptr<resource>对象的过程,程序也不需要作太多的考虑,只要保证resource对象是使用shared_ptr智能指针管理的就可以了。

总之,工厂方法shared_from_this()是使用模板方法模式实现的,而模板模式又是使用CRTP惯用法来实现的。

参考:
https://cloud.tencent.com/developer/article/2362395

相关文章:

  • 网络安全·第五天·TCP协议安全分析
  • leetcode0207. 课程表-medium
  • WordPress 只能访问html文件,不能访问php
  • (最新)华为 2026 届校招实习-硬件技术工程师-硬件通用/单板开发—机试题—(共14套)(每套四十题)
  • flutter 插件收集
  • 联易融出席深圳链主企业供应链金融座谈会,加速对接票交所系统
  • AI 模型在前端应用中的典型使用场景和限制
  • Activity使用优化
  • Elasticsearch性能优化实践
  • Nacos 2.0.2 在 CentOS 7 上开启权限认证(含 Docker Compose 配置与接口示例)
  • linux 手动触发崩溃
  • 马浩棋:产通链CT-Chain 破局不动产 RWA,引领数智金融新变革
  • 企业微信私域运营,基于http协议实现SCRM+AI完整解决方案
  • 3.1.1 MaterialDesign中DrawerHost使用案例
  • 【架构】ANSI/IEEE 1471-2000标准深度解析:软件密集型系统架构描述推荐实践
  • 【网络】代理服务器收尾及高级IO
  • 组网技术-BGP技术,IS-IS协议,VRRP技术
  • 路由与路由器
  • Kubernetes finalize | namespace卡Terminatingfinalizers删除失败
  • 5G + 物联网:智能世界的催化剂,如何用Python打造下一代IoT应用?
  • 王励勤当选中国乒乓球协会新一任主席
  • 00后为购演唱会门票转账近16万元“解封”银行卡,民警及时追回
  • “五一”假期前多地规范旅游市场:要求明码标价,禁止强迫购物
  • “女孩被前男友泼汽油烧伤致残案”二审择期宣判
  • 四川苍溪警方通报一男子离家出走:遗体被打捞上岸,排除刑案
  • “雷公”起诉人贩子王浩文案将开庭:索赔6元,“讨个公道”