19.8 C++STL标准模板库大局观-适配器概念、分类、范例与总结

本文主要是介绍19.8 C++STL标准模板库大局观-适配器概念、分类、范例与总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

19.1 C++STL标准模板库大局观-STL总述、发展史、组成与数据结构谈
19.2 C++STL标准模板库大局观-容器分类与array、vector容器精解
19.3 C++STL标准模板库大局观-容器的说明和简单应用例续
19.4 C++STL标准模板库大局观-分配器简介、使用与工作原理说
19.5 C++STL标准模板库大局观-迭代器的概念和分类
19.6 C++STL标准模板库大局观-算法简介、内部处理与使用范例
19.7 C++STL标准模板库大局观-函数对象回顾、系统函数对象与范例
19.8 C++STL标准模板库大局观-适配器概念、分类、范例与总结

文章目录

  • 8.适配器概念、分类、范例与总结
    •   8.1 适配器基本概念
    •   8.2 容器适配器
    •   8.3 算法适配器
    •   8.4 迭代器适配器
    •   8.5 总结

8.适配器概念、分类、范例与总结

在这里插入图片描述

  8.1 适配器基本概念

    可能有些读者不好理解适配器是什么,以及它是用来做什么的。适配器类似于转接头这种概念。假如有一个设备,没有办法接到另外一个设备上,这个设备接口窄,另外一个设备接口宽,此时就需要一个转接头做一个桥梁,把这个设备接到另外一个设备上,这就是转接头的作用。
    回归到本节的主题,适配器到底是什么?读者可以这样理解:把一个既有的东西进行适当的改造,如增加一点东西,或者减少一点东西,就会成为一个适配器。光听这个解释,可能还是有点难以理解,后面会举例讲解。
    适配器分很多种,不是单纯的一种,下面就按种类讲一讲各种不同的适配器。

  8.2 容器适配器

    前面学习过容器中的deque(双端队列),也学习了stack(堆栈)以及queue(队列)。讲解时曾经说过,deque包含了stack的能力,也包含了queue的能力。或者换句话说,stack和queue是包含了deque的部分能力,如图所示。
在这里插入图片描述
    前面讲解的时候,是把stack和queue归类为容器(顺序容器),而根据适配器的基本概念,既然stack和queue都属于把既有的deque进行适当的改造,减少了一点东西(阉割),所以,在这里正式把stack和queue归类为容器适配器(从原来的容器分类中拿掉,增加到适配器分类中)。
    可以观察一下queue的实现源码,在开头包含如下头文件:

#include <queue>

    在main主函数中输入queue并将光标定位在这个单词中间位置按F12键,屏幕下方会出现一堆查找符号的结果,单击第一个,就可以跳转到queue的源码。

template<class _Ty,class _Container = deque<_Ty> >class queue{	// FIFO queue implemented with a container
public:...void push(const value_type& _Val){	// insert element at beginningc.push_back(_Val);}
protected:_Container c;	// the underlying container};

    通过queue的源码,可以看到它与deque是有密切关系的,其中的push成员函数用于把数据扔到队列的末尾,push成员函数直接调用的是c.push_back(…);而c就是_Container,_Container就是deque。也就是说,queue的push功能就是deque的push_back功能。

  8.3 算法适配器

    通过前面的讲解知道了算法,算法是一个函数模板,或者看成是一个函数。
    而算法适配器可以叫作函数适配器。算法适配器最典型的就是绑定器(Binder),所以看一看绑定器。
    从老的STL版本里可能看到过bind1st、bind2nd等(函数模板),C++11里全变成了bind,bind在后面的章节会详细讲解。bind也是一个函数模板,被归类为算法适配器中的绑定器。下面将提供一个例子,逐步引导读者看一看绑定器的使用。
    在main主函数中,加入如下代码:

{vector<int> myvector = { 50,15,80,30,46,80 };//统计某个值出现的次数int cishu = count(myvector.begin(), myvector.end(), 80); //算法,统计出现80这个元素的次数cout << cishu << endl; //2,表示80这个值出现了2次
}

    还有一个名字叫作count_if的算法,是根据一定的条件进行统计。因为count算法的功能比较有限,只能统计某个元素,笔者希望程序更灵活一些,先在main主函数前面增加一个类A的定义:

class A
{
public:bool operator()(int i){return i > 40; //希望大于40的元素就被统计}
};
int main()
{A myobja;cishu = count_if(myvector.begin(), myvector.end(), myobja);cout << cishu << endl; //结果等于4,4个元素>40
}

    这时想起了一个标准库中提供的函数对象less(),如果能借用这个来实现相同的功能,就不用自己写一个函数对象(可调用对象)了。
    前面提到过,less()这个可调用对象在调用的时候是需要两个形参的(注意,less本身是类模板,实际是其中的operator()需要两个形参)。
    如果想把less中operator()的实现用到上面的范例中来,取代上面类A中的operator(),那怎样取代呢?必须要把less中operator()的两个参数变成一个参数才能使用。要完成这个功能,就需要用到算法适配器中的绑定器
    试想,如果less中operator()的两个参数其中一个绑定到40,这时可以认为less的operator()就剩一个参数了。这里需要用的正是1个参数的operator(),所以,焦点就集中在通过bind把less中operator()的一个参数绑定到40。
    输入less并按下F12键,观察一下less的源码。如下:

template<class _Ty = void>struct less{constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const{	// apply operator< to operandsreturn (_Left < _Right);}};

    比对类A中的operator(),因为less中的operator()的return这行代码是一个小于号“<”,而类A中operator()中的return是个大于号“>”,所以先把类A中的operator()中的return这行代码改写一下。改写代码后的类A如下:

class A
{
public:bool operator()(int i){//return i > 40; //希望大于40的元素就被统计return 40 < i;}
};

    现在比较一下,类A中operator()里的40就对应less中operator()里的_Left(第一个参数),类A中operator()里的i就对应less中operator()里的_Right(第二个参数)。这样代码就好写了(下面代码看不懂没有关系,下一章会详细讲解bind的用法):

bind(less<int>(), 40, placeholders::_1);//less<int>中operator()的第一个参数绑了一个40,当调用这个less<int>()可调用对象时,
//less<int>中operator()的第二个参数,也就是这个placeholders::_1表示 在调用这个可调用对象时,被传入的第一个参数所取代

    如果上面这行代码中的注释没看懂,可以尝试看看下面这两行代码:

auto bf = bind(less<int>(), 40, placeholders::_1); //less<int>中operator()的第一个参数绑定了40(第一个参数值为40)
bf(19);   //19是bf的第一个参数,调用时就传递给了less<int>中operator()作为其第二个参数(第二个参数值为19)

    回过头来,改造代码,引入bind适配器,配合less实现上述同样功能(大于40的元素就被统计),只需要修改count_if所在行代码。修改后的代码行如下:

cishu = count_if(myvector.begin(), myvector.end(), bind(less<int>(), 40, placeholders::_1)); //临时对象less<int>()

    执行起来,结果一切正常。
    上面的代码执行时count_if会调用less的operator(),并提供一个参数(该参数来自于myvector容器中的元素),而提供的这一个参数正好对应着less中operator()的第二个参数(less中operator()的第一个参数已经固定绑定为40)。把这行代码拆分一下:
  · bind:算法(函数)适配器中的绑定器。代码实现上是一个函数模板
  · less():是一个函数对象(仿函数),这里是一个临时对象
  · count_if:是一个算法
    这个范例很好地演示了算法适配器中的绑定器。可以看到,bind具有比较强的能力,可以把某些参数绑住,其他的参数保持“需要被提供”的状态。该绑定器与函数对象配合起来就可以在很多场合省下程序员自己来写函数对象的时间。

  8.4 迭代器适配器

    这里只准备举一个迭代器适配器的例子。在13.9.3节的第二个话题中讲解了反向迭代器,这其实就是一个迭代器适配器。回顾一下当时写的范例:

{vector<int> iv = { 100,200,300 };for (vector<int>::reverse_iterator riter = iv.rbegin(); riter != iv.rend(); riter++){cout << *riter << endl;}
}

  8.5 总结

    还有其他种类的适配器,不过很多都比较抽象,用的场合也不多,所以笔者就不在这里一一介绍了,读者以后遇到时可以慢慢研究摸索。
    至此,就把STL的几大组成部分全部讲解到了。现在读者对STL的整个组成应该有一个比较具体的了解了。当然,本书不是专门讲解STL的书籍,所以,笔者也没有面面俱到地把STL的细节都讲出来。既然本章讲解的是STL标准模板库大局观,那就是要先从大处着眼来了解STL,然后具体下来,能够使用一些常用的STL功能。同时,当遇到不常见到的问题时,能够查阅相关的资料去解决,这就达到了本章的学习目的。
    现在,完整的STL组成结构图如图19.32所示。

在这里插入图片描述

这篇关于19.8 C++STL标准模板库大局观-适配器概念、分类、范例与总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/159464

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决