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++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

Java利用Spire.Doc for Java实现在模板的基础上创建Word文档

《Java利用Spire.DocforJava实现在模板的基础上创建Word文档》在日常开发中,我们经常需要根据特定数据动态生成Word文档,本文将深入探讨如何利用强大的Java库Spire.Do... 目录1. Spire.Doc for Java 库介绍与安装特点与优势Maven 依赖配置2. 通过替换

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C# List.Sort四种重载总结

《C#List.Sort四种重载总结》本文详细分析了C#中List.Sort()方法的四种重载形式及其实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录1. Sort方法的四种重载2. 具体使用- List.Sort();- IComparable

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

SpringBoot项目整合Netty启动失败的常见错误总结

《SpringBoot项目整合Netty启动失败的常见错误总结》本文总结了SpringBoot集成Netty时常见的8类问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、端口冲突问题1. Tomcat与Netty端口冲突二、主线程被阻塞问题1. Netty启动阻

SpringBoot整合Kafka启动失败的常见错误问题总结(推荐)

《SpringBoot整合Kafka启动失败的常见错误问题总结(推荐)》本文总结了SpringBoot项目整合Kafka启动失败的常见错误,包括Kafka服务器连接问题、序列化配置错误、依赖配置问题、... 目录一、Kafka服务器连接问题1. Kafka服务器无法连接2. 开发环境与生产环境网络不通二、序

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u