二、C++、STL标准模板库和泛型编程 ——适配器、补充(侯捷)

news2024/11/24 20:54:45

侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! !
一、C++ 面向对象高级开发
1、C++面向对象高级编程(上)
2、C++面向对象高级编程(下)
二、STL 标准库和泛型编程
1、分配器、序列式容器
2、关联式容器
3、迭代器、 算法、仿函数
4、适配器、补充
三、C++ 设计模式
四、C++ 新标准
五、C++ 内存管理机制
六、C++ 程序的生前和死后

二、C++、STL标准模板库和泛型编程 ——适配器、补充 (侯捷)

  • 适配器(Adapters)
    • 容器适配器(Container Adapters)
    • 仿函数适配器(Functor Adapters)
      • bind2nd(绑定第二实参)
      • not1
      • bind(新型适配器)
    • 迭代器适配器(Iterator Adapters)
      • reverse_iterator
      • inserter
    • X适配器
      • ostream_iterator
      • istream_iterator
  • 补充
    • Hash Function
    • tuple
    • type traits
    • cout
    • movable

使用一个东西,却不明白它的道理,不高明!—— 林语堂

阶段学习
使用C++标准库
认识C++标准库(胸中自有丘壑!)
良好使用C++标准库
扩充C++标准库

所谓 Generic ProgrammingGP,泛型编程),就是使用 template (模板)为主要工具来编写程序。

  • GP 是将 datasmethods 分开来;

    • ContainersAlgorithms可各自闭门造车﹐其间以Iterator连通即可·
    • Algorithms通过Iterators确定操作范围﹐也通过Iterators取用 Container元素。
  • OOP(Object-Oriented Programming),企图将 datasmethods 关联在一起。

C++标准模板库Standard Template 最重要的六大部件(Components):容器算法仿函数迭代器适配器分配器

  • 容器(Containers)是class template
  • 算法(Algorithms)是function template其内最终涉及元素本身的操作,无非就是比大小!)
  • 迭代器(Iterators)是class template
  • 仿函数(Functors)是class template
  • 适配器(Adapters)是class template
  • 分配器(Allocators)是class template

关系图:
在这里插入图片描述

适配器(Adapters)

可以把它理解为改造器,它要去改造一些东西;也可以理解为实现换肤功能

已经存在的东西,改接口,改函数名等。。。

在这里插入图片描述

实现适配,可以使用继承(is a)复合(has a) 的两种方式实现。
共性:STL使用 复合(has a) 来实现适配!

容器适配器(Container Adapters)

例如:stackqueue

具体定义查看:序列式容器的stackqueue容器

  • 只使用一部分以及改接口,改函数名等。。。
  • 复合(内涵) 的东西换一个风貌换一种风格出来!

仿函数适配器(Functor Adapters)

bind2nd(绑定第二实参)

把东西记起来,以备后面使用!

可以看到下面的这个例子,使用算法count_if:

  • 第三个参数是一个predicate,也就是判断条件,有一个仿函数对象less<int>(),但是他被仿函数适配器bind2nd(将less的第二个参数绑定为 40)和 not1(取反)修饰,从而实现判断条件为是否小于40

在这里插入图片描述
bind2nd调用binder2nd:

  • 图上灰色的东西就是仿函数适配器仿函数之间的问答
    • 这里就体现了仿函数为什么要继承适合的unary_function或者binary_function等类的原因!
  • 还有一个细节:适配器适配之后的仿函数也能够继续被适配:
    • 所以适配器要继承unary_function或者binary_function等类,这样才能回答另外一个适配器的问题。
    • 问 bianry_fucntion 三个参数first_argument_typesecond_argument_typeresult_type
    • 提问前面都要加上typename,是为了让编译通过!
  • 所以,仿函数必须能够回答适配器的问题,这个仿函数才是可适配的!

相对绑定第二实参,绑定第一实参bind1st

not1

对一个Predicate取反。

  • not1是构造一个与谓词结果相反的一元函数对象。
  • not2是构造一个与谓词结果相反的二元函数对象。

一层套一层,像乐高积木一样!

在这里插入图片描述

bind(新型适配器)

替换了一些过时(bind1st、bind2st)的仿函数适配器!

在这里插入图片描述
std::bind 可以绑定:

  1. functions
  2. function objects
  3. member functions_1(占位符号)必须是某个object地址。
  4. data members_1必须是某个object地址。

返回一个function object ret。调用ret相当于调用上述的1,2,3或者相当于取出4.

示例:

// bind example
#include <iostream>     // std::cout
#include <functional>   // std::bind

// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}

struct MyPair {
  double a,b;
  double multiply() {return a*b;}
};

int main () {
  // 占位符的使用方法!!!!!!!!
  using namespace std::placeholders;    // adds visibility of _1, _2, _3,...
  
  //---------------------绑定function,也就是前面的1---------------------
  // binding functions:
  auto fn_five = std::bind (my_divide,10,2);               // returns 10/2
  std::cout << fn_five() << '\n';                          // 5

  auto fn_half = std::bind (my_divide,_1,2);               // returns x/2
  std::cout << fn_half(10) << '\n';                        // 5

  auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x
  std::cout << fn_invert(10,2) << '\n';                    // 0.2

  auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)
  std::cout << fn_rounding(10,3) << '\n';                  // 3

  MyPair ten_two {10,2};

  //---------------------绑定member functions,也就是前面的3---------------------
  // binding members:
  //member function 其实有一个看不见的实参argument :this
  auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
  std::cout << bound_member_fn(ten_two) << '\n';           // 20

  //---------------------绑定member data,也就是前面的4---------------------
  auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
  std::cout << bound_member_data() << '\n';                // 10


  //-------------------------上面的bind2nd就可以替换了-------------------------
  vector<int> v {15, 37, 94, 50, 73, 58, 28, 98};
  int n = count_if(v.cbegin(), v.cend(), not1(bind2nd(less<int>(), 50)))
  cout << "n=" << n << endl; //5

  //替换
  auto fn_ = bind(less<int>(), _1, 50);
  cout << count_if(v.cbegin(), v.cend(), fn_) << endl;   //3
  
  return 0;
}

迭代器适配器(Iterator Adapters)

reverse_iterator

reverse_iterator
rbegin(){//取逆向的头,就是正向的尾巴
	return reverse_iterator(end());
}
	
reverse_iterator
rend(){//取逆向的尾巴,就是正向的头
	return reverse_iterator(begin());
}

也有五种关联类型:

在这里插入图片描述

inserter

可以不用担心copy到的目的容器大小不匹配的问题。
copy是写死的,我们调用copy,希望完成在容器指定位置插入一些值!具体的实现:

  • 把相应的容器和迭代器传入inserter,对容器的迭代器中的 = 运算符进行重载,就能改变copy的行为!
  • 因为这个是对迭代器的 = 运算符行为进行重定义,所以是迭代器的适配器

在这里插入图片描述

X适配器

X表示未知:(容器迭代器函数,三大类之外的!)

  • 包括ostreamistream迭代器适配器

ostream_iterator

  • copy都是已经写好的,不能再改了!
  • 该适配器适配的是basic_ostream,也是重载了 = 运算符,添加输出操作

在这里插入图片描述

istream_iterator

ostream_iterator的兄弟,cin >> x被替换为了 x = *iit ,适配 basic_istream

  • 不断++,就不断读内容。

在这里插入图片描述
copy都是已经写好的,不能再改了!

当创建iit(cin),已经读入数据了!
不断++,就不断读内容。

在这里插入图片描述

补充

标准库STL周边还有一些东西需要知道。

Hash Function

如果有一个我们自己的类,我们要怎么给这个类设计hash函数呢?

在这里插入图片描述

使用类中的成员变量的hash函数得到hash值,然后相加,(下面左上角)这个太naive了,可能会产生很多冲突。
所以使用右边那个!

  • args是C++11的新特性,任意多个参数都行,n个参数的args作为另外一个函数的输入的时候
    • 先调用①,分配一个种子seed,再调用②;
    • 在②里面拆分args,拆分成1 + n-1 的形式,递归调用自身,直到args只剩下一个参数时,调用③;
    • 在②中拆分时,会不断改变种子seed基本类型的hash函数 + 0x9e3779b9 + ... (越乱越好,没有数学可言,)。

也是使用想法一的思想,但是加入了更多的复杂的操作,使得得到的hash code冲突更少。

在这里插入图片描述

tuple

一组东西的组合,可以任意指定多少个元素,这些元素可以是任意的类型。

使用示例:

tuple<string, int, int, complex<double>> t;
sizeof(t); // 32, 为什么是32,而不是28呢?啊~侯捷也无法理解啊!

tuple<int, float, string> t1(41, 6.3, "test");
cout << "t1:" << get<0>(t1) << ' ' << get<1>(t1) << ' ' << get<2>(t1) << endl;

auto t2 = make_tuple(22, 44, "test2"); // t2也是一个tuple,自动推导类型

tuple_size< tuple<int, float, string> >::value; // 3
tuple_element< tuple<int, float, string> >::type; // 取tuple里面的类型

继承的是自己,会自动形成一个类的继承关系,注意有一个空的 tuple 类。

在这里插入图片描述

type traits

trivial:琐碎的,平凡的,平淡无奇的,无关痛痒的,无价值的,不重要的。

泛化模板类,包括五种比较重要的typedef: 默认的回答都是重要的!

typedef _false_type has_trivial_default_constructor; 		//默认构造函数是不重要吗?
typedef _false_type has_trivial_copy_constructor;			//拷贝构造函数是不重要嘛?
typedef _false_type has_trivial_assignment_operator;		//拷贝赋值构造函数是不重要嘛?
typedef _false_type has_trivial_destructor;					//析构函数是不重要嘛?
typedef _false_type is_POD_type;							//是不是旧格式(struct,只有数据,没有方法)?

比如说对于inttype traits五个问题的回答都不重要。一般是算法会对traits进行提问。实用性不高。

在这里插入图片描述
type traits

现在的 traits机 ,非常智能:

  • 只要把自己写的或者系统自带的类,作为 is_()::value 就能得到问题的答案,这些问题包括下面几种,不全:

测试:

//global function template
template <typename T>
void type_traits_output(const T& x)
{
	cout << "\ntype traits for type:" << typeid(T).name() << endl;
	
	cout << "is_void\t" << is_void<T>::value << endl;
	cout << "is_integral\t" << is_integral<T>::value << endl;
	cout << "is_array\t" << is_array<T>::value << endl;
	cout << "is_class\t" << is_class<T>::value << endl;
	cout << "is_function\t" << is_function<T>::value << endl;
	cout << "is_pointer\t" << is_pointer<T>::value << endl;
	cout << "is_object\t" << is_object<T>::value << endl;
	...
}

在这里插入图片描述
在这里插入图片描述

类型萃取机这么强的功能,是怎么实现的呢?下面以is_void为例:

  • 首先去掉 constvolatile 这两种对得到类特征无用的修饰关键字,做法如下(主要是用模板技巧);
  • 然后将去掉 cv (就是constvolatile )关键字之后,再传入 __is_void_helper 模板类中,让其自己匹配是不是空类型,匹配到不同的模板类,返回不同的bool值。

在这里插入图片描述

cout

是一个对象object,不是一个类,源码如下:

  • 想要用cout输出自己的类型,就可以重载 << 运算符。

在这里插入图片描述

movable

movable元素会对各种容器的速度效能产生影响!!!
vector的增长方式,对vector的影响很大,对其他的容器影响不是很大!

moveable 指的是 move 构造、move 赋值

  • move():是一种浅层拷贝,当用 a 初始化 b 后,a 不再需要时,最好是初始化完成后就将 a 析构,使用 move 最优。
  • 如果说,我们用 a 初始化了 b 后,仍要对 a 进行操作,用这种浅层复制的方法就不合适了。

所以C++引入了移动构造函数,专门处理这种,用 a 初始化 b 后,就将 a 析构的情况。这种操作的好处是:

  • a 对象的内容复制一份到 b 中之后,b 直接使用 a 的内存空间,这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷。

move的使用场景是:原来的对象不再使用。如果再用就很危险!!!

在这里插入图片描述

在这里插入图片描述

测试函数:

移动构造函数实现是:调用拷贝构造函数,但是会将原来的对象中的成员变量置0!这样就不会调用原对象的析构函数了!如下图加深的部分,而且用的是引用的引用 &&&&右值引用,右值有一个很重要的性质:只能绑定到一个将要销毁的对象。

调用移动构造函数方法,显示调用move:classObj_1(std::move(classObj_2))

move焊copy:

**容器的move**

注:仅供学习参考,如有不足,欢迎指正!

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

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

相关文章

时序预测 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络时间序列预测

时序预测 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络时间序列预测 目录 时序预测 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-CNN-LSTM贝叶斯优…

c++标准模板(STL)(std::array)(四)

定义于头文件 <array> template< class T, std::size_t N > struct array;(C11 起) std::array 是封装固定大小数组的容器。 此容器是一个聚合类型&#xff0c;其语义等同于保有一个 C 风格数组 T[N] 作为其唯一非静态数据成员的结构体。不同于 C 风格数…

MySQL 高可用实战

文章目录 前言实现思路环境准备一、实现MySQL高可用1.1 修改配置文件1.2 MySQL监控脚本1.4 重启keepalived1.5 查看虚拟ip 二、高可用验证2.1 模拟宕机2.2 查看虚拟ip2.3 连接MySQL2.4 恢复主机2.5 查看虚拟ip 总结 前言 请各大网友尊重本人原创知识分享&#xff0c;谨记本人博…

基于脉搏波的疾病诊断:准确率接近100%的未来前景?

脉搏波分析作为一种无创诊断方法&#xff0c;在糖尿病诊断领域具有巨大潜力。某些研究表明&#xff0c;基于脉搏波的诊断方法在准确性方面可能优于传统血糖检测方法。然而&#xff0c;在将这种方法应用于临床之前&#xff0c;我们需要关注其稳定性、易用性、成本效益等因素。 首…

老胡的周刊(第089期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 MOSS[2] 来自复旦大学的开源工具增强型会话语…

翻译: 迁移学习和微调 Transfer learning fine-tuning

1. 介绍 迁移学习包括获取在一个问题上学到的特征&#xff0c;并将它们用于一个新的类似问题。例如&#xff0c;已经学会识别浣熊的模型的特征可能有助于启动旨在识别狸猫的模型。 迁移学习通常用于您的数据集数据太少而无法从头开始训练全尺寸模型的任务。 在深度学习的背景…

AI模型推理(1)——入门篇

前言 本文主要介绍AI模型推理的相关基础概念&#xff0c;为后续云原生模型推理服务的学习做准备。 初识模型部署 对于深度学习模型来说&#xff0c;模型部署指让训练好的模型在特定环境中运行的过程。相比于常规的软件部署&#xff0c;模型部署会面临更多的难题&#xff1a; …

SPI机制详细讲解

文章目录 SPI机制案例分析建立DriverManager建立MysqlDriver来实现扩展建立OracleDriver来实现扩展测试spitest 源码分析ServiceLoader类的结构reload加载类LazyIterator类parse解析URL对象方法parseLine方法 SPI机制 SPI &#xff0c;全称为 Service Provider Interface&…

Bridge模式如何配置

Bridge模式案例&#xff08;一&#xff09; 基于Docker引擎启动Nginx WEB容器&#xff0c;默认以Bridge方式启动Docker容器&#xff0c;会动态DHCP给Docker容器分配IP、网关等信息&#xff0c;操作指令如下&#xff1a; 查看镜像列表 docker images#运行新的Nginx容器 dock…

ChatGPT实现代码解释

代码解释 新手程序员在入门之初&#xff0c;最好的学习路径就是直接阅读其他人的代码&#xff0c;从中学会别人是怎么写的&#xff0c;为什么这么写。过去&#xff0c;这个学习过程可能需要广泛阅读官方文档&#xff0c;在 GitHub issue 上提问&#xff0c;上 Stack Overflow …

内网渗透(六十)之AS-REP Roasting攻击

AS-REP Roasting攻击 AS-REP Roasting是一种针对用户账户进行离线爆破的攻击方式。但是该攻击方式使用上比较受限,因为其需要用户账户设置“不要求Kerberos预身份验证”选项。而该选项默认是没有勾选的。Kerberos域身份验证发生在Kerberos身份验证的第一阶段(AS_REQ&AS_…

手记系列之四 ----- 关于使用MySql的一些经验

前言 本篇文章主要介绍的关于本人在使用MySql记录笔记的一些使用方法和经验&#xff0c;温馨提示&#xff0c;本文有点长&#xff0c;约1.5w字&#xff0c;几十张图片&#xff0c;建议收藏查看。 一、MySql安装 下载地址:https://dev.mysql.com/downloads/ 在安装MySql之前&a…

我在VScode学Java(Java一维数组)

我的个人博客主页&#xff1a;如果\真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;(我在Vscode学Java) 我在VScode学Java(Java一维数组&#xff09; Java 一维数组 声明数组&#xff1a;先声明&#xff0c;后使用 动态分配内…

最长连续子序列---双指针

一、最长连续不重复子序列 核心思路&#xff1a; 遍历数组a中的每一个元素a[i], 对于每一个i&#xff0c;找到j使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列&#xff0c;长度为i - j 1, 将这一长度与r的较大者更新给r。 对于每一个i&#xff0c;如何确定…

XR技术在手术中的应用调研

虚拟现实、增强现实、混合现实等概念和技术是最近几年发展起来的&#xff0c;相信你对去年大火的元宇宙深有感触&#xff0c;元宇宙属于虚拟现实的技术范畴&#xff0c;头号玩家电影也让虚拟现实走进大众的视野中。早在2015年&#xff0c;笔者参加一次展会时就有接触&#xff0…

【数据库复习】第四章数据库恢复技术

一、事务 定义 一个数据库操作序列 一个不可分割的工作单位&#xff08;要么全做&#xff0c;要么不做&#xff09; 恢复和并发控制的基本单位 事务和程序比较 在关系数据库中&#xff0c;一个事务可以是一条或多条SQL语句,也可以包含一个或多个程序。 一个程序通常包含…

java倒序输出数字的方法

1.在输入框中输入一个整数&#xff0c;比如要输入“5”&#xff0c;需要输出倒序&#xff0c;可以使用数字键盘进行输入&#xff0c;也可以使用文本编辑器进行输入。 2.在命令行中输入“6”&#xff0c;如图所示。 3.选择一个字符串作为例子。使用字符串编辑器中的 reverse命令…

这个 Chrome 插件,让你的 ChatGPT 不再报错

ChatGPT的官网最近几天报错越来越频繁了&#xff0c;相信大家都发现了。 一旦你离开页面时间比较久&#xff0c;再度返回跟它进行对话&#xff0c;就会出现如下报错&#xff1a; 虽然这个报错信息以前也出现过&#xff0c;但现在的频率确实过高&#xff0c;对于每天需要使用 C…

“火灾不分昼夜,安全在我心中”——五一前厂房消防检查纪实

检查人员: Scott, Jason, Willson, Hanson 检查时间: 2023年4月28日 检查地点: 1厂房、2厂房室内外 检查内容: 一、室内外消火栓: 室内栓外观正常&#xff1b; 室外栓: 栓体防冻防尘套破损、遗失&#xff0c;消防栓缺少防撞保护&#xff1b; 按规定距离厂房外墙不宜小于5…

gitlab部署及整合Jenkins持续构建(四)sonarqube9.9安装和使用(一步一坑)

文章目录 postgresql13.0安装1、配置postgresql数据库2、进入postgresql创建数据库 代码质量管理平台--sonarqube安装1、前置依赖下载2、安装unzip并解压sonarqube并移动到/usr/local&#xff1a;3、修改sonarqube相应的配置4、新增用户&#xff0c;并将目录所属权赋予该用户&a…