罗剑锋的 C++ 实战笔记
罗剑锋
前奇虎 360 技术专家,Nginx/OpenResty 开源项目贡献者
34779 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 32 讲
结束语 (1讲)
罗剑锋的 C++ 实战笔记
15
15
1.0x
00:00/00:00
登录|注册

20 | 设计模式(下):C++是怎么应用设计模式的?

你好,我是 Chrono。
上节课,我谈了设计模式和设计原则。今天,我就具体说说,在 C++ 里,该怎么应用单件、工厂、适配器、代理、职责链等这些经典的设计模式,用到的有 call_once()、make_unique()、async() 等 C++ 工具,希望能够给你一些在实际编码时的启发。
(在接下来学的时候,你也可以同时思考一下它们都符合哪些设计原则,把设计模式和设计原则结合起来学习。)

创建型模式

首先来看看创建型模式,它隐藏了类的实例化过程和细节,让对象的创建独立于系统的其他部分
创建型模式不多,一共有 5 个,我觉得最有用的是单件和工厂
单件很简单,要点在于控制对象的创建数量,只能有一个实例,就像是公司的 CEO 一样,有且唯一。
关于它的使用方式、应用场景,存在着一些争议,但我个人觉得,它很好地体现了设计模式的基本思想,足够简单,可以作为范例,用来好好学习模式里的各个要素。
关于单件模式,一个“老生常谈”的话题是“双重检查锁定”,你可能也有所了解,它可以用来避免在多线程环境里多次初始化单件,写起来特别繁琐。
使用第 14 讲里提到的 call_once,可以很轻松地解决这个问题,但如果你想要更省事的话,其实在 C++ 里还有一种方法(C++ 11 之后),就是直接使用函数内部的 static 静态变量。C++ 语言会保证静态变量的初始化是线程安全的,绝对不会有线程冲突。比如:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《罗剑锋的 C++ 实战笔记》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(12)

  • 最新
  • 精选
  • 有一个感想。。所有设计模式,都是为了隔离变化,所谓的设计模式的教程其实在教我们: 编程时,哪些地方要注意变化,如何应对变化。

    作者回复: 理解的非常对,设计模式的一个核心思想就是引入中间层,隔离变化,尽量减少代码的变动。

    3
    25
  • eletarior
    又提起了单例模式,我个人是很反对单例模式的,其缺点大于优点:破坏了程序的封装,可以随便传来传去的多可怕,有全局变量的全部缺点(如果你需要使用全局变量,这说明你的设计有很大的问题),违背了单一职责原则,难以单元测试,依赖不清等;实在要使用单例模式的话,一定要注意原则:如果你使用单例是因为某个类的实例不能超过一个,那么这通常是可以的。如果使用它是因为singleton是一个全局可访问的对象,它可以让您避免考虑与其他对象如何相互通信,以及每个对象负责什么,那就是你用错了! 适配器类 继承于某个类,然后对这个类进行接口改造,使之能适配另一个类的对其调用; 装饰模式在不改变对象接口的前提下强化对象的功能; 与适配器模式提供不同的接口以适配调用不同,代理模式则是提供相同的接口,代替宿主完成某些功能,

    作者回复: 说的很好,单件易学,用起来简单,所以就很容易被滥用,这个是需要特别注意的。 但从学习设计模式的角度来看,它是一个很好的范例。

    16
  • 有学识的兔子
    1、可以根据需要,通过不同的参数定制化,产生不同的对象;同时还可以对参数类型范围进行检查。 2、相同之处在于都是对于原始类或对象的二次封装; 不同在于: 适配器是为了解决接口不兼容的问题,提供新接口来间接改造原始接口; 装饰器:与原始类继承同样的父类,为原始类提供了额外增强的功能接口,且可以嵌套多个装饰器; 代理:个人感觉和装饰器非常像,但代理的作用更偏向于类或对象本身功能之外的能力,例如统计调用次数/运行时间,有点管理原始类的意思。

    作者回复: 对,总结的很好。 很多设计模式的结构都很相似,因为用的都是对象组合技术,但区别就在于它们的使用方式、目的、用途。

    6
  • 泰伦卢
    感谢老师,这篇文章读下来收获还是蛮多的,但文中提到静态变量初始化是线程安全得,前提貌似应该加个c++11后,避免争议

    作者回复: 感谢提醒,稍后会联系编辑做个补充。

    6
  • KevinJ
    Meyer's singleton代码段是不是没写全,应该是这样吧: template<typename T> static auto& getInstance() { static T obj; return obj; }

    作者回复: great

    3
  • henry
    其实在开发过程中,很多时候已经用了上面的一些模式,只是当时没想到是这个名字。一些常用的模式平时我们在开发过程中也会自己摸索总结出来。 比如我的一个项目是需要提供一套通用的接口给上层调用者,而接口内部需要不断地对接业务功能相似、实现方式各不相同的第三方的服务,在代码迭代过程中自然就会重构成适配器模式和代理模式,这样上层调用者不用关心第三方服务的不同,也不能越过去直接外部通讯,整个系统就会比较健壮。 代理模式:经常写的包装类,应该就属于这个模式的实践。

    作者回复: 是的,设计模式的目的和意义就在于把经验总结下来,避免“重复发现”,节约我们的时间和精力。

    2
  • eddy
    设计模式给我的感觉就是对软件工程的封装,真的有点忽悠人的感觉,另外设计模式算是从cpp发展而来,但是讲的最多的反而是java程序员,这是为什么呢?

    作者回复: 设计模式可真不是忽悠,而是确确实实的真理,已经融合到了许多语言里了。 因为设计模式书出版在1994年,那时候最成熟的面向对象语言就是C++,而后来的java的面向对象更存粹一些,所以java用设计模式多也就很自然了。

    归属地:广东
    2
    1
  • Stephen
    可以这样理解适配器和工厂模式的区别吗? 适配器中讲的例子,转换前后的本质没有变化,都是装数据的"容器",只是表示形式发生了改变. 而工厂模式,则原料和最终的产品本质上是两种东西,比如原料是值和类型,而产品是指向该类型变量的指针.

    作者回复: 不太准确。 适配器是结构型模式,对原有对象进行组合整理,得到新的对象,是对对象的一种包装。 工厂模式是创建型模式,是“凭空”创建出新对象,解决的是如何按需求生成对象。 设计模式并不与C++里的容器、指针直接关联,在学设计模式的时候不要被语言细节给困住了,尽量跳出来,在高一个层级来思考。

    2
    1
  • 杜跃信
    文中提到函数内静态变量的线程安全性问题 似乎还要看编译器的支持程度吧,记得以前看到vs2015的资料上明确表示,这种方式不是线程安全的,像vs这种对c++支持很高的编译器都不支持,不知道其它编译器实现如何,如果用的时候还要去查编译器的资料,我觉得还不如避免使用来得安全

    作者回复: 在C++11里已经明确定义了,static变量的初始化是线程安全的。

    1
  • Geek_7c0961
    核心思想是隔离变动的部分, 重用稳定的部分

    作者回复: great

    归属地:美国
收起评论
显示
设置
留言
12
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部