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

相关文章

Python标准库datetime模块日期和时间数据类型解读

《Python标准库datetime模块日期和时间数据类型解读》文章介绍Python中datetime模块的date、time、datetime类,用于处理日期、时间及日期时间结合体,通过属性获取时间... 目录Datetime常用类日期date类型使用时间 time 类型使用日期和时间的结合体–日期时间(

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

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

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