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

相关文章

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

Win32下C++实现快速获取硬盘分区信息

《Win32下C++实现快速获取硬盘分区信息》这篇文章主要为大家详细介绍了Win32下C++如何实现快速获取硬盘分区信息,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实现代码CDiskDriveUtils.h#pragma once #include <wtypesbase

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

C++ Primer 标准库vector示例详解

《C++Primer标准库vector示例详解》该文章主要介绍了C++标准库中的vector类型,包括其定义、初始化、成员函数以及常见操作,文章详细解释了如何使用vector来存储和操作对象集合,... 目录3.3标准库Vector定义和初始化vector对象通列表初始化vector对象创建指定数量的元素值

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::