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

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

如何自定义一个log适配器starter

《如何自定义一个log适配器starter》:本文主要介绍如何自定义一个log适配器starter的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求Starter 项目目录结构pom.XML 配置LogInitializer实现MDCInterceptor

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio