题外话:侯捷老师难得一年就来上九堂课就要会宝岛,特此留念签名赠语及合照以自勉。

 学海无涯,为勤是岸

<正文开始>

  昨天晚上连上了3个小时的大课探究单单讲了Adapter一个类,幸运的是本人恰好在大一的时候接触过比如<functioinal>库类中的bind1st,bind2nd这些函数对象的使用方法,毕竟若要使用<algorithm>的话,里面几乎每一个函数都需要我们把模版中的函数对象比如Comparato之类的重写一下,但是真的没有想到,这些库这些类使用起来明明那么简单但是在老师的讲解下里面的结构和设计人员的思路原来真的是复杂缜密。

  具体说Adapter(适配器)这种设计之前必须要先简单说说LSP(里氏替换原则),其内容是:子类(derived typed)必须能够替换掉它的基类(base type)。这一句话就有点让我摸不着头脑了,既然是子类已经从基类继承过来了,那岂不就是子类必然能够替换掉父类么?不然多态(polyporphism)还从和谈起?如果是在java里的话,(我觉得)也许这点是毫无争议的因为java有强大的继承体系一切类都要从Object类继承而来(lambda表达式的目标对象也不是一种类,所以不算从Object继承而来),然而在C++语言之中另有一种独特的设计--private继承,这种独特的继承方式会是父类中无论是protected还是public继承抑或是多重继承过来的指针引用都转变为子类的private数据,也就是说这些数据在继承树的这条分支上已经被宣告了终结。所以象在MFC这样的库的建立的时候LSP原则是必须遵守的,不然会给程序带来(严重的)隐患。

课件中所示,Adapter设计一般有三种(昨天只讲了两种):

(1)Container Adapter

  这个库的底层容器我们可以看到选择的是deque,值得注意的是在这套设计之中queue和deque是复合的关系,但是和我猜测的相反,queue包含了deque类也就是说queue是在deque之上架构起来的,至于为什么明明deque和(双驱)list有相同的“效果”甚至有相同的方法命名却选择了deque来作为默认的底层容器,老师谦虚地讲他也不敢下定论,但是我们至少可以大胆地推测deque的速度或者效率是优于list的。而且在这里面queue“HAS-A”deque这样的关系,在这个结构之中架构起来的更多的容器基本上都是以底层容器出发,改写方法添加约束从而完成架构。在《More Effective C++》中的编程准则之中有提到过,建议编程者将一切的数据写入private之中,但是这并不意味着这个类在继承链上部分数据的残缺,因为我们会在public或者protected中声明它们的getter&setter(是否想起了java呢:-P)来不断地继承过来对这个数据的操作权从而实现安全架构。

##stack容器的实现和queue的实现是极其相似的,这里不多赘述##

(2)Function Adapter

  这部分较上部分较难懂的,例子中的Adapter简单的来讲就是要实现一个功能:参数绑定。有的函数对象作为参数可能只需要一个参数来进行调用,但是我们这里却只用一个双参的函数对象无法直接使用作为参数,那么最效率的解决方法就是使用Adapter。当然不要自信地认为会用了这个binder系列not系列函数就是理解了Adapter,深入了解架构,才能帮助我们写出自己想要的Adapter,了解STL的规范才可以帮助我们写出的Adapter更好地与上层容器/上层对象相兼容。比如要写出一个三参绑为一个四参绑为两个这些特殊的情况才会有良好的方案去解决。

  例子是假设我们要使用count_if函数,这个函数在<algorithm>库中的作用是if xx then count计数,其中的第一个参数和第二个参数当然是Iterator类型,然而第三个参数我们需要一个单参的Predicate函数对象或者函数指针。如下图所示,其实在这里面Predicate只是template中的一个命名,它可以是T,Fxxx各种名字,但是一个Predicate的名字就可以让其他人看出这个函数对象最后需要返回的是一个布尔值,如果一旦模版实例化为一个将操作符重载为例如void operator()(Paramlist..)这样的函数时,那么在count_if内部的判断分支语言就会遇到麻烦,而且由于c++的灵活性如果将返回值重载为int /long类的话,这样count_if就不会报错程序照常执行的同时,会给貌似正确程序埋下隐患。

  那么Adapter是怎么样架构起来的呢?下面这张图很好地概括了下来。如下图所示(其实那个f和x之间按理来说应该有个逗号Anyway~)Adapter的实现过程就是在构造的时候将需要绑定的参数绑定下来,然后重载操作符使之成为一个新的函数对象。是的,貌似非常简单,但是接着向下看,如果考虑到functionAdapter是从unary_function和binary_function继承而来的模版结构的话,会大大增加复杂度,整个搭建起来adapter的过程也是不断地向父类“询问”typedef类型的过程,这些约束是adapter中最难理解的部分。

 最后我们再来看一下具体的实现宏观过程:

##课后提问##

我:在C++11的新特性中已经允许了自动类型推断,而为什么黑板上强调的bind2nd的模版类型必须和参数类型表明一致?:::(黑板)bind2nd<less<int>>(less<int>(),12)

侯捷:因为C++11的自动类型推断只能运用在模版函数之中,但是对于模板函数对象,我们必须前后表明参数和函数对象的模版。

<软件架构与设计模式>侯捷老师关于Adapter类在STL中的深入解析和模式探讨的更多相关文章

  1. Adapter类 调用Activity中的函数

    在Adapter类中可以定义一个MainActivity变量,在初始化时,对其赋值,例如fragment的适配器中: private MainActivity context; private Lis ...

  2. 侯捷老师C++大系之C++面向对象开发:(一)不带指针的类:Complex复数类的实现过程

    一.笔记1.C++编程简介 2.头文件与类的声明 防卫式声明#ifndef __COMPLEX__#define __COMPLEX__ …… #endif头文件的布局模板简介template< ...

  3. vector源码3(参考STL源码--侯捷):pop_back、erase、clear、insert

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷):空间分配.push_back vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 v ...

  4. vector源码(参考STL源码--侯捷):空间分配导致迭代器失效

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  5. vector源码2(参考STL源码--侯捷):空间分配、push_back

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  6. vector源码1(参考STL源码--侯捷):源码

    vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector源码3(参考STL源 ...

  7. 侯捷《C++面向对象开发》——动手实现自己的复数类

    前言 最近在看侯捷的一套课程<C++面向对象开发>,刚看完第一节introduction之后就被疯狂圈粉.感觉侯捷所提及所重视的部分也正是我一知半解的知识盲区,我之前也写过一些C++面向对 ...

  8. 侯捷STL学习(一)

    开始跟着<STL源码剖析>的作者侯捷真人视频,学习STL,了解STL背后的真实故事! 视频链接:侯捷STL 还有很大其他视频需要的留言 第一节:STL版本和重要资源 STL和标准库的区别 ...

  9. 侯捷STL课程及源码剖析学习3: 深度探索容器list

    一.容器概览 上图为 GI STL 2.9的各种容器.图中以内缩方式来表达基层与衍生层的关系.所谓的衍生,并非继承(inheritance)关系,而是内含(containment)关系.例如 heap ...

随机推荐

  1. Spring Security三种认证

    Spring Security: 1.用户名+密码认证 2.手机号+短信认证 Spring Social: 1.第三方认证, QQ登录等 Spring Security OAuth: 1.把认证之后的 ...

  2. jar包解压与打包

    首先感谢大神的指导:https://blog.csdn.net/mr_pang/article/details/47028921 1.首先准备一个能运行的jar文件,我们使用第三方解压工具进行解压wi ...

  3. CSS div固定顶端

    position: fixed;原来只需要这么一个设置就可以!

  4. Java线程—-Runnable和Callable的区别和联系

    Java 提供了三种创建线程的方法 1.继承Thread接口 public class Thread2Thread { public static void main(String[] args) { ...

  5. 【Atheros】pktgen的ipv6地址处理函数参考及ipv6基本知识

    pktgen有很多函数可以作为很好的网络相关的工具函数,这里列出ipv6中1:0:0:0:0:0:0:1和1::1这两种地址形式相互转化的工具函数. 第一个函数,用于把一个1:0:0:0:0:0:0: ...

  6. iOS_3_图片浏览

    终于效果图: BeyondViewController.h // // BeyondViewController.h // 03_图片浏览 // // Created by beyond on 14- ...

  7. Windows下搭建React Native Android开发环境

    准备工作 安装JDK 安装Android SDK 安装C++环境 安装node.js 安装react-native命令行工具 创建项目 运行packager 运行模拟器 安卓运行 安卓调试 安装JDK ...

  8. Linux进程间通信(IPC)机制总览

    Linux进程间通信 Ø  管道与消息队列 ü  匿名管道,命名管道 ü  消息队列 Ø  信号 ü  信号基础 ü  信号应用 Ø  锁与信号灯 ü  记录锁 ü  有名信号灯 ü  无名信号灯(基 ...

  9. ios 手势返回<1>2

    iOS-给push出来的控制器添加全局滑动(返回)手势   在iOS中,当我们push出一个新的控制器的时候,我们可以向右拖拽屏幕的左边缘来返回(pop)到上一级控制器,但是这个功能有两个缺陷: 当自 ...

  10. 【原创】Hibernate自动生成(1)

    本实战是博主初次学习Java,分析WCP源码时,学习HibernateTools部分的实战,由于初次接触,难免错误,仅供参考,希望批评指正. 开发环境: Eclipse Version: Photon ...