侯捷 C++ STL标准库和泛型编程 —— 8 适配器

news2024/11/20 7:26:29

8 适配器

  • 适配器 Adapter 只是一个小变化,比如改个接口,函数名称等等
  • 其出现在三个地方:仿函数适配器,迭代器适配器,容器适配器
  • 可以使用继承 / 复合的两种方式实现,STL中都用复合

其思想就是将该记的东西记起来,以便日后使用

8.1 容器适配器

stackqueue 都是属于 deque 的 Adapter

比如 stack 中将 deque 的 push_back 改名为 push

8.2 函数适配器

8.2.1 binder2nd

binder2nd —— 绑定第二参数

// 数范围内所有小于40的元素个数
cout << count_if(vi.begin(), vi.end(), 
                 bind2nd(less<int>(), 40));
// 辅助函数bind2nd,使用方便
// 编译器自动推动op的类型(函数模板)
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x)
{
	typedef typename Operation::second_argument_type arg2_type;
	// 调用ctor生成一个binder2nd临时对象并返回
	return binder2nd<Operation>(op, arg2_type(x)); 
}


// binder2nd适配器:将二元函数对象转换为一元函数对象
template <class Operation>
class binder2nd 
	: public unary_function<typename Operation::first_argument_type,
	                        typename Operation::result_type>
// 可能binder2nd也要被改造,要回答问题
{
protected:
	Operation op; // 内部成员,记录op和第二实参
	typename Operation::second_argument_type value;
public:
	binder2nd(const Operation& x, 
			  const typename Operation::second_argument_type& y)
		: op(x), value(y) {} // ctor,将op和第二实参记录下来
	typename Operation::result_type
		operator()(const typename Operation::first_argument_type& x) const
	{
		return op(x, value); // 实际调用op,第二实参为value
	}
};

当然还有:binder1st —— 绑定第二参数

新型适配器:bind,代替了 bind1stbind2ndbinder1stbinder2nd

8.2.2 not1

not1 —— 否定

// 数范围内所有大于等于40的元素个数
cout << count_if(vi.begin(), vi.end(), 
    			not1(bind2nd(less<int>(), 40)));
8.2.3 bind

C++11提供的 Adapter,其可以绑定:

  1. functions
  2. function objects
  3. member functions
  4. data members

测试函数 / 对象

// functions
double my_divide(double x, double y)
{
	return x/y;
}

// function objects 测试与functions同理
// divides<double> my_divide;

struct MyPair
{
    // data members
	double a, b;
    // member functions
	double multiply()
	{
		return a*b;
	}
};

占位符 placeholders

using namespace std::placeholders;

提供了 _1_2_3,·······

下面的的 _1 指的是被绑函数中的第一个参数

  • binding functions / function objects 测试

    • 单纯将两个整数 102 绑定到 my_divide

      auto fn_five = bind(my_divide, 10, 2);
      cout << fn_five() << endl; // 5.0
      
    • _1 占据第一参数,第二参数绑定2,即 x/2

      auto fn_half = bind(my_divide, _1, 2);
      cout << fn_half(10) << endl; // 5.0
      
    • _1 占据第一参数,_2 占据第二参数,即 y/x

      auto fn_invert = bind(my_divide, _2, _1);
      cout << fn_invert(10, 2) << endl; // 0.2
      
    • bind 指定了一个模板参数 int,将 my_divide 的返回类型变为 int,即 int(x/y)

      auto fn_rounding = bind<int>(my_divide, _1, _2);
      cout << fn_rounding(10, 3) << endl; // 3
      
  • binding member functions / data members 测试

    MyPair ten_two {10, 2}; 用C++11的新语法定义一个实例

    • 绑定 member functions,由于成员函数有 this,所以 _1 就相当于 this,即 x.multiply()

      auto bound_memfn = bind(&MyPair::multiply, _1);
      cout << bound_memfn(ten_two) << endl; // 20
      
    • 绑定 data members,绑定是谁的数据

      把实例 ten_two 绑定到 a,即 ten_two.a

      auto bound_memdata = bind(&MyPair::a, ten_two);
      cout << bound_memdata() << endl; // 10
      

      用占位符绑定,即 x.a

      auto bound_member_data2 = bind(&MyPair::b, _1);
      cout << bound_member_data2(ten_two) << endl;
      

8.3 迭代器适配器

8.3.1 reverse_iterator
image-20230922162253063

注意:对逆向迭代器取值,就是取其所指正向迭代器的前一个位置

template <class Iterator>
class reverse_iterator
{
protected:
	Iterator current;
public:
	// 五个associated types与对应的正向迭代器相同

	typedef Iterator iterator_type; // 代表正向迭代器
	typedef reverse_iterator<Iterator> self; // 代表逆向迭代器
public:
	explicit reverse_iterator(iterator_type x) : current(x) {}
	reverse_iterator(const self& x) : current(x.current) {}

	iterator_type base() const { return current; } // 取出正向迭代器
	
    // 对逆向迭代器取值,就是取其所指正向迭代器的前一个位置
	reference operator*() const 
	{ Iterator tmp = current; return *--tmp; }

	pointer operator->() const { return &(operator*()); } // 同上

	// 前进变后退,后退变前进
	self& operator++()
	{ --current; return *this; }
	self& operator--()
	{ ++current; return *this; }
	self operator+(difference_type n)const
	{ return self(current-n); }
	self operator-(difference_type n)const
	{ return self(current+n); }
};
8.3.2 inserter

对于 copy(InputIterator first, InputIterator last, OutputIterator result),其会不管 OutputIterator 后是否有充裕空间,对 result 开始依次赋值

但如果使用 inserter,就会有如下用 copy 实现的插入的效果

image-20230922165235291

list<int> foo, bar;
for (int i = 1; i <= 5; i++)
{
    foo.push_back(i);
    bar.push_back(i*10);
}

list<int>::iterator it = foo.begin();
advance(it, 3);

copy(bar.begin(), bar.end(), inserter(foo, it));

注:其是 output_iterator_tag

其实现原理核心就是 —— 对 =操作符重载

insert_iterator<Container>&
operator=(const typename Container::value_type& val)
{
	// 关键:转调用insert()
	iter = container->insert(iter, val);
	++iter; // 使其一直随target贴身移动
	return *this;
}

8.4 X适配器

8.4.1 ostream_iterator

其会将 copy 变为一个输出工具,分隔符是 ,

vector<int> vec = { 1,2,3,4,5,6,7,8,9,10 };

ostream_iterator<int> out_it(cout, ",");
copy(vec.begin(), vec.end(), out_it); // 1,2,3,4,5,6,7,8,9,10,

其核心依然是操作符重载,这样就相当于 cout<<*first; cout<<",";

basic_ostream<charT,traits>* out_stream;
const charT* delim;

...
    
ostream_iterator<T, charT, traits>& operator=(const T& value)
{
	*out_stream << value;
	if(delim!=0) *out_stream << delim; // 分隔符delimiter
	return *this;
}

ostream_iterator<T,charT,traits>& operator*(){return *this;}
ostream_iterator<T,charT,traits>& operator++(){return *this;}

...

其中 out_stream 存的 coutdelim 存的 ,

8.4.2 istream_iterator

例一:

创建 iit 的时候就已经把所有的键盘输入读进去了,之后就是一个一个取出来赋值给 value 的操作

double value1, value2;
istream_iterator<double> eos; // end of stream iterator
istream_iterator<double> iit(cin); // 相当于cin>>value
if(iit != eos)
    value1 = *iit; // 相当于return value
iit++; // 迭代器不断++,就是不断地读内容
if(iit != eos)
    value2 = *iit;

例二:

cin 读 data,插入到目的容器

istream_iterator<double> eos; // end of stream iterator
istream_iterator<double> iit(cin);

copy(iit, eos, inserter(c,c.begin()));

原理依旧是大量的**操作符重载 **—— 就可以改变原函数的作用

basic_istream<charT, traits>* in_stream;
T value;

...
    
istream_iterator():in_stream(0){} // eos
istream_iterator(istream_type& s):in_stream(&s){++*this;} // 进++

istream_iterator<T,charT,traits,Distance>& operator++()
{
    if(in_stream && !(*in_stream >> value)) // 开始读了
        in_stream = 0;
    return *this;
}
const T& operator*() const { return value; }

...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1059172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

U盘插上就显示让格式化是坏了吗?

U盘以其体积小巧、存储容量大、读写速度快的特点&#xff0c;在各种工作和个人使用场合中得到了广泛应用&#xff0c;因此深得用户好评。然而&#xff0c;在日常使用U盘的过程中&#xff0c;经常会遇到一些问题和挑战。今天&#xff0c;我将为大家详细解释U盘出现要求格式化的现…

嵌入式Linux应用开发-驱动大全-同步与互斥③

嵌入式Linux应用开发-驱动大全-同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占(pree…

用于工业物联网和自动化的 Apache Kafka、KSQL 和 Apache PLC4

由于单一系统和专有协议&#xff0c;数据集成和处理是工业物联网&#xff08;IIoT&#xff0c;又名工业 4.0 或自动化工业&#xff09;中的巨大挑战。Apache Kafka、其生态系统&#xff08;Kafka Connect、KSQL&#xff09;和 Apache PLC4X 是以可扩展、可靠和灵活的方式实现端…

MATLAB算法实战应用案例精讲-【优化算法】雪融优化器(SAO)(附MATLAB代码实现)

前言 算法原理 算法步骤 ①初始化阶段: 与大多数智能算法相似,就是随机生成一批粒子: ②探索阶段 当雪或由雪转化的液态水转化为蒸汽时,由于不规则的运动,搜索代理呈现出高度分散的特征。在这项研究中,布朗运动被用来模拟这种情况。作为一个随机过程,布朗运动被广…

讲讲项目里的仪表盘编辑器(四)分页卡和布局容器组件

讲讲两个经典布局组件的实现 ① 布局容器组件 配置面板是给用户配置布局容器背景颜色等属性。这里我们不需要关注 定义文件 规定了组件类的类型、标签、图标、默认布局属性、主文件等等。 // index.js import Container from ./container.vue; class ContainerControl extends…

六、vpp 流表+负载均衡

草稿&#xff01;&#xff01;&#xff01; vpp node其实就是三个部分 1、plugin init 2、set command 3、function 实现功能&#xff0c;比如这里的流表 今天我们再用VPP实现一个流表的功能 一、流表 1.1流表----plugin init VLIB_REGISTER_NODE 注册流表节点 // 注册流…

15-自动化测试——理论知识

目录 1.什么是自动化测试&#xff1f; 2.常见的自动化测试分类 2.1.单元测试&#xff08;Java、Python&#xff09; 2.2.接口测试&#xff08;Java、Python&#xff09; 2.3.UI测试&#xff08;移动端、网站&#xff09; 3.如何实施自动化测试&#xff1f; 4.自动化测试…

测开 | Vue速查知识点

文章目录 Vue知识1. Vue 概述2. Vue 代码格式3. Vue 指令3.1 v-bind & v-model3.2 v-on3.3 v-if和v-show3.4 v-for 4. 生命周期 Vue知识 1. Vue 概述 简介&#xff1a; Vue.js&#xff08;读音 /vjuː/, 类似于 view&#xff09; 是一套构建用户界面的 渐进式框架。与其他…

[QT编程系列-44]: Windows + QT软件闪退的检测方法

目录 一、Windows程序闪退的问题定位方法 1.1 Windows程序闪退 1.2 要找到Windows程序的crash点 1.3 当Windows程序崩溃时&#xff0c;可以尝试以下方法获取出错信息&#xff1a; 二、关键工具的进一步分析 2.1 Windows事件查看器&#xff08;Event Viewer&#xff09; …

openGauss学习笔记-87 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用简单概述

文章目录 openGauss学习笔记-87 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用简单概述87.1 授予用户权限87.2 创建/删除MOT87.3 为MOT创建索引 openGauss学习笔记-87 openGauss 数据库管理-内存优化表MOT管理-内存表特性-使用MOT-MOT使用简单概述 使用…

【python的输入】sys.stdin与sys.argv

在老师的课堂里碰到了sys.stdin与sys.argv&#xff0c;虽然是很简单的东西&#xff0c;还是花了大半天的时间才勉强理解。在这里记录一下学习过程&#xff0c;方便以后用到复习。 一、sys.stdin 根据python3 library里的解释&#xff0c; sys.stdin可用于所有交互式的输入。 …

Vscode爆红Delete `␍`eslintprettier/prettier

一、先看报错 文件中爆红&#xff0c;提示 Delete ␍eslintprettier/prettier 二、解决方案 项目根目录下&#xff0c;.prettierrc.js 文件中&#xff1a; endOfLine: auto,三、重启VsCode 此时不在爆红&#xff0c;问题完美解决

STM32CubeMX学习笔记-USB接口使用(HID按键)

STM32CubeMX学习笔记-USB接口使用&#xff08;HID按键&#xff09; 一、USB简介1.1 USB HID简介 二、新建工程1. 打开 STM32CubeMX 软件&#xff0c;点击“新建工程”2. 选择 MCU 和封装3. 配置时钟4. 配置调试模式 三、USB3.1 参数配置3.2 引脚配置3.3 配置时钟3.4 USB Device…

数据分析与挖掘: 红楼梦人物关系(Python)词云图

一: 角色剧本 第一代&#xff1a;水字辈祖宗创下基业 贾源、贾演兄弟二人帮先帝打江山立下战功&#xff0c;贾演被封为宁国公&#xff08;大约有平定江山安宁天下之意&#xff09;&#xff0c;贾源被封荣国公&#xff08;大约有强国富民之功&#xff09;。贾源贾演二兄弟皆是一…

项目进展(五)-修复PCB电路板,学习32位ADC芯片ADS1285

一、前言 上个月29号放假了&#xff0c;和朋友一起去了南京(人是真滴多)&#xff0c;师兄晚放假几天&#xff0c;结果在测试时不小心把12V和GND碰触到一起了&#xff0c;导致12V短路&#xff0c;电路板几乎瘫痪了。 今天下午到学校之后就开始着手寻找问题和修复&#xff0c;最…

Kafka日志索引详解以及生产常见问题分析与总结

文章目录 1、Kafka的Log日志梳理1.1、Topic下的消息是如何存储的&#xff1f;1.1.1、 log文件追加记录所有消息1.1.2、 index和timeindex加速读取log消息日志。 1.2、文件清理机制1.2.1、如何判断哪些日志文件过期了1.2.2、过期的日志文件如何处理 1.3、Kafka的文件高效读写机制…

腾讯云服务器南京地域详细介绍、测试IP和Ping值测速

腾讯云服务器南京地域怎么样&#xff1f;南京地域很不错&#xff0c;正好处于中间的位置&#xff0c;南方北方用户均可以选择&#xff0c;网络延迟更低速度更快&#xff0c;并且目前南京地域有活动&#xff0c;南京地域可用区可选南京一区、南京二区和南京三区&#xff0c;腾讯…

软件设计模式系列之二十四——模板方法模式

在软件设计领域&#xff0c;设计模式是一组被反复使用、多次实践验证的经典问题解决方案。其中&#xff0c;模板方法模式是一种行为型设计模式&#xff0c;用于定义一个算法的骨架&#xff0c;将算法中的某些步骤延迟到子类中实现&#xff0c;从而使子类可以重新定义算法的某些…

互联网Java工程师面试题·Zookeeper 篇·第二弹

目录 13. 服务器角色 14. Zookeeper 下 Server 工作状态 15. 数据同步 16. zookeeper 是如何保证事务的顺序一致性的&#xff1f; 17. 分布式集群中为什么会有 Master&#xff1f; 18. zk 节点宕机如何处理&#xff1f; 19. zookeeper 负载均衡和 nginx 负载均衡区别 20…

代码随想录算法训练营第五十七天 | 392.判断子序列 115.不同的子序列

1. 判断子序列 392. 判断子序列 - 力扣&#xff08;LeetCode&#xff09; dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度。 class Solution {public boolean isSubsequence(String s, String t) {//dp[i][j] 表示…