C++【适配器】【仿函数】【deque结构了解】【反向迭代器】学习

news2024/9/17 8:25:56

目录

priority_queue       

适配器

适配器初认识

模板参数的缺省值

仿函数

priority_queue 向上调整算法&&向下调整算法

为什么需要使用仿函数

函数指针方式

仿函数方式 

仿函数较于函数指针的优点 

函数指针的调用实现

仿函数的调用实现

使用仿函数

deque

背景

deque结构

deque迭代器

指针类型与迭代器的比较

 反向迭代器

非类型模板参数


priority_queue       

本质就是一个堆,它是适配器,不是容器,没有迭代器,优先级队列的插入就是堆的插入,堆插入向上调整


适配器

适配器初认识

适配器的本质就是复用,比如在实现栈的时候可以通过利用vector来完成

适配器的作用是能够使两个不相同的类型,使用同一功能,比如栈和vector

适配器实现过程:适配器类则实现目标接口,并使用适配者类的方法来提供所需的功能,在此间,栈就可以使用vector的功能

我们可以通过下面类模板中多提供了一个参数class Container 这个参数,通过main函数里的lhl::stack<int, vector<int>> s; 来表明我的栈是通过vector来实现的,这就是数组栈

namespace lhl {
	template<class T,class Container>
	class stack {
	private:
		Container _con;
	public:
		void push(const T& x) {
			_con.push_back(x);
		}

		void pop() {
			_con.pop_back();
		}

		T& top() {
			return _con.back();
		}

		bool empty() {
			return _con.empty();
		}

		size_t size() {
			return _con.size();
		}
	};
}
int main() {
	lhl::stack<int,vector<int>> s;    //数组栈
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);
	while (!s.empty()) {
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;
	return 0;
}

甚至可以通过修改容器参数来实现其它结构的栈 

eg:lhl::stack<int, list<int>> s;        这是链式栈

模板参数的缺省值

函数参数可以用缺省参数,模板参数也同样可以用缺省参数,模板参数传的是类型

 在STL文档中,stack的模板第二个参数的缺省值传的就是deque<T>,模板参数是在编译的时候传参,但并不是随便填一个容器都能够完成适配器!!!适配器构造,析构和拷贝都不用我们自己去写

deque的方括号效率不高


仿函数

priority_queue 向上调整算法&&向下调整算法

void adjust_up(int child) {
	int parent = (child - 1) / 2;
	while (child > 0) {
		if (_con[child] > _con[parent]) {
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}
}

void adjust_down(int parent) {
	int child = parent * 2 + 1;

	while (child < _con.size()) {
		if (_con[child + 1] < _con.size() &&
                 _con[child] < _con[child + 1]) {
			child = child + 1;
		}

		if (_con[child] > _con[parent]) {
			swap(_con[child], _con[child]);
			parent = child;
			child = parent * 2 + 1;
		}

		else {
			break;
		}
	}
}

这种算法调整是属于大堆的

那假如我的要求是小堆呢?

难不成需要额外在写一次向上调整和向下调整吗?

就算我们在写一次,我们再调用的时候,还是需要去更改调用对象啊,所以肯定是及其繁琐且无意义的

因此,我们可以通过仿函数来控制 

为什么需要使用仿函数

仿函数也叫做函数对象,仿函数最重要的实现步骤的是重载()!!!,即重载圆括号

先来看看为什么要用仿函数,如果我们使用函数指针我们又该如何 

函数指针方式

bool lessfc(int x,int y) {
	return x < y;
}

bool greaterfc(int x, int y) {
	return x > y;
}

class A {
public:
	A(bool(*pf)(int,int))
		:_pf(pf)
	{}

	void func(int xx, int yy) {
		cout << "void func(int xx, int yy): " << _pf(xx, yy) << endl;
	}

	bool (*_pf)(int, int);
};

仿函数方式 

namespace LHL {
	template<class T>
	class less {
	public:
		bool operator()(const T& x, const T& y) {
			return x < y;
		}
	};

	template<class T>
	class greater {
	public:
		bool operator()(const T& x, const T& y) {
			return x > y;
		}
	};
}

template<class Compare>
class B {
public:

	void func(int xx, int yy) {
		Compare com;
		cout << "void func(int xx, int yy): " << com(xx, yy) << endl;
	}
};

仿函数较于函数指针的优点 

 仿函数在此处的优点就是:类型简单,它就是一个普通的类型

而函数指针需要则需要在类里面传进来,因此需要一个成员将它存储起来,在上例中,bool (*_pf)(int, int);就是一个存储函数指针的成员,此外还需要通过构造函数将其传递进来。

而仿函数则只需要给个自定义对象,就可以调用重载()的比较效果

函数指针的调用实现
void test3() {
	A a1(lessfc);
	a1.func(1, 2);

	A a2(greaterfc);
	a2.func(4, 5);
}
仿函数的调用实现
void test4() {
	B<LHL::less<int>> a1;
	a1.func(1, 2);

	B<LHL::greater<int>> a2;
	a2.func(1, 2);
}

 明显能够对比出仿函数可以直接通过对象就可以调用,而函数指针还需要传进去才可以使用

函数指针是个对象,不能够在类模板的参数中去传

使用仿函数

所以我们在自己手写priority_queue的时候,可以将模板参数扩充到三位

template<class T,class Container = vector<T>>

template<class T,class Container = vector<T>,class Compare = less<T>>

或者

template<class T,class Container = vector<T>,class Compare = greater<T>>

回头看,我们就会发现

stack queue 和 priority_queue本质上都是仿函数

stack  和 queue 的默认容器是deque

priority_queue的默认容器时vector


deque

背景

vector和list的优缺点

vector优点:(连续存储的优势)

1,下标的随机访问

2,缓存命中更厉害

vector缺点:

1,插入删除较前的位置,效率较低

2,扩容消耗高

list优点:

1,任意位置插入删除效率高

2,按需申请,消耗较小

list缺点:

1,随机访问效率低

2,缓存命中率低

我们可以看出,vector和list的优势和劣势是相反互补的,那么存不存在一张容器,能够既有list的优点,又有vector的优点

既然都这样问了,那答案肯定是有的

deque就是这种情况下,就又有vector的优点,也有list的优点 

deque结构

deque的结构是指针与数组并用,一般情况下,deque会开几个数组规模大小相同的buffer,再通过一个中控 (存储buffer的指针数组)来对buffer进行管理,它是中控满了才进行扩容

deque迭代器

deque的迭代器有四个指针

first指向的是一个buffer的开始

last指向的是一个buffer的结束

cur指向的是buffer中的一个位置

node指向存储当前buffer指针在中控的位置,node相当于二级指针

deque有两个迭代器

class deque{
    iterator start;
    iterator finish;
}

start指向的是第一个buffer,并且其cur指向的是这个buffer的第一个位置

finish指向的是最后一个buffer,并且其cur指向的是这个buffer的最后一个数据的下一个位置

前插和尾插都是通过修改start和finish来实现,如果buffer满了,则修改中控的位置,如果中控满了则扩充中控

优势是头插头删,尾插尾删很方便

结论:下标随机访问,效率不错,但跟vector仍有差距,中间插入删除效率差

所以这就是为什么stack和queue会选择它


指针类型与迭代器的比较

ListNode<int>* it1;
__list_iterator<int, int&, int*> it;

它们两个不是同一类型,第一个是内置类型第二个是自定义类型

它们的解引用,++的意义也不一样,相差很大

++后地址都发生了变化,但他们的刚开始所指向的却是一样的,且都拥有8字节的大小(指针大小)

it1++是根据Node的大小来跨越,it2++后是指链表的下一个位置,因为链表的物理空间不是连续的,所以++后的下一个节点的地址就会变得不确定


 反向迭代器

我们可以通过跟实现正向迭代器一样来实现反向迭代器,但是设计者认为STL中有很多容器,难道都要一一实现,既然一个容器的正向迭代器已经完成,那么可不可以跟用适配器一样,采用迭代器适配器,同意建立一个类,来实现基本上容器的反向迭代器,至于是什么类型的迭代器,就让迭代器模板去推测吧

基本功能都是在正向迭代器内完成了,即对链表内部的处理等,再此只需复用即可

Iterator能够确认类型,所以第一个模板参数由class T 演变成 class Iterator 是经得起推敲的

//适配器,是谁的适配器我不管,我也不知道
template<class Iterator,class Ref,class Ptr>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator,Ref,Ptr> Self;
	Iterator cur;

	ReverseIterator(Iterator it)
		:cur(it)
	{}

	Self& operator++() {
		--cur;
		return *this;
	}

	Ref operator*() {
		Iterator tmp = cur;	//不能用Self,用了就是自己减自己了
		--tmp;
		return *tmp;
	}

	Ptr operator->() { 
		return &(operator*());
	}

	bool operator!=(const Self& s) {
		return cur != s.cur;
	}

    ……
};

*rit取到的是前一个位置,即要减减一下,如果不这样,对rbegin解引用的时候,就会变成对哨兵位解引用

typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;

		reverse_iterator rbegin() {
			return reverse_iterator(end());
		}

		reverse_iterator rend() {
			return reverse_iterator(begin());
		}

 测试,引入头文件 #include"ReverseIterator.h"

写出来的反向迭代器类不仅list能用,vector也能用,其它类也能用

所以实现一个类,基本上的其他类都能够复用


非类型模板参数

在实现类模板过程中,我们有时会出现比较固定的类

#define N 10
template<class T>
class Stack {
private:
	T _a[N];    //10
	int _top;

public:
	void fun() {	}
};

int main() {
	Stack<int> s1;
	return 0;
}

比如此类,如果我想看一个20个空间的_a[N],我们便会较为尴尬,就是不能够达到我可以随意定义我想要的空间大小

所以,就出现了非类型模板参数

template<class T,size_t N>
class Stack {
private:
	T _a[N];
	int _top;

public:
	void fun() {}
};

int main() {
	Stack<int,10> s1;
	Stack<int,30> s2;
	return 0;
}

非类型模板参数只能是整型常量,浮点数那些也不行

以上就是本次博文的学习内容了,如有错误,还望各位大佬指点,谢谢阅读!

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

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

相关文章

Java 入门指南:Java 并发编程 —— 线程隔离技术 ThreadLocal

线程隔离技术 线程隔离是一种多线程编程技术&#xff0c;它可以将数据或资源在不同线程之间进行隔离&#xff0c;保证每个线程使用的数据或资源是独立的&#xff0c;不会互相干扰。线程隔离通常应用于高并发场景下&#xff0c;可以有效提升系统性能并提高并发能力。 实现方式…

MySQL record 03 part

插入表数据&#xff1a; 一般情况下&#xff0c;向表中添加新的记录&#xff0c;应该包含此表的所有字段&#xff0c;也就是应该给表的所有字段添加值&#xff0c; 1.使用insert into语句&#xff0c;指定字段名&#xff08;可以是所有的字段&#xff0c;也可以是某几个字段&am…

Android Framework(四)WMS-窗口显示流程——窗口创建与添加

文章目录 流程概览涉及模块流程概览 应用端——window创建&#xff1a;Activity::attach创建window流程setWindowManager&#xff0c;getWindowManagerDecorView 应用端——window的显示流程&#xff1a;Activity::onResumeViewRootImpl::setViewmWindowSession 是什么mWindow是…

【数据库】MySQL聚合统计

目录 1.聚合函数 案例1&#xff1a; 统计班级共有多少同学 案例2&#xff1a;统计本次考试的数学成绩分数个数 案例3&#xff1a;统计数学成绩总分 案例4&#xff1a;统计平均总分 案例5&#xff1a;返回英语最高分 案例6&#xff1a;返回 > 70 分以上的数学最低分 2.分…

双指针思想

一.双指针思想 1.分类&#xff1a;同向双指针&#xff0c;反向双指针 2.优点&#xff1a;可以将两层循环嵌套的问题优化成一层循环 3.常见情况 <1>利用快慢双指针确定链表的中间节点&#xff0c;链表是否带环&#xff0c;带环链表的入环点在哪里 <2>一次循环解…

Android Fragment 学习备忘

1.fragment的动态添加与管理&#xff0c;fragment生命周期在后面小节&#xff1a;https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_sourcecopy_web&vd_source982a7a7c05972157e8972c41b546f9e4https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_…

安装Android Studio及第一个Android工程可能遇到的问题,gradle下载过慢、sync失败?

Android Studio版本众多&#xff0c;电脑操作系统、电脑型号、电脑硬件也是多种多样&#xff0c;幸运的半个小时内可以完成安装&#xff0c;碰到不兼容的电脑&#xff0c;一天甚至更长时间都无法安装成功。 Android安装及第一个Android工程分为4个步骤&#xff0c;为什么放到一…

9.8笔试记录

1.在c中哪些运算符不能重载? 在 C 中&#xff0c;有以下几个运算符不能被重载&#xff1a; . &#xff1a;成员访问运算符。例如obj.member中的.不能被重载。 :: &#xff1a;作用域解析运算符。用于指定命名空间、类等的作用域&#xff0c;不能被重载。 ?: &#xff1…

spring揭秘19-spring事务01-事务抽象

文章目录 【README】【1】事务基本元素【1.1】事务分类 【2】java事务管理【2.1】基于java的局部事务管理【2.2】基于java的分布式事务管理【2.2.1】基于JTA的分布式事务管理【2.2.2】基于JCA的分布式事务管理 【2.3】java事务管理的问题 【3】spring事务抽象概述【3.1】spring…

easyExcel-读取Excel

1、简单读取&#xff0c;没有合并单元格 2、复杂读取&#xff0c;合并单元格-方法一 1、简单读取&#xff0c;没有合并单元格 1.1、引入pom文件 <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.…

【单片机】详细解析完全重映射和部分重映射

1、重映射的作用 单片机中的每个引脚通常都有多个功能&#xff0c;不仅仅是作为普通的输入或输出&#xff0c;还可以与某些外设&#xff08;如定时器、串口、SPI、I2C等&#xff09;关联。默认情况下&#xff0c;这些外设功能通常固定绑定到特定的GPIO引脚。但是&#xff0c;在…

Matplotlib颜色透明度设置

matplotlib中的透明度设置都是通过alpha设置的,一般在能设置颜色的包括背景色、图表色、文字颜色都可以设置透明度 float类型,取值范围为[0.0,1.0],alpha取值越小越透明 import numpy as np import matplotlib.pyplot as pltx np.linspace(0, 2*np.pi, 100) y np.sin(x) y1…

Arch - 架构安全性_授权(Authorization)

文章目录 OverView授权&#xff08;Authorization&#xff09;RBAC&#xff1a; 概述RBAC&#xff1a;基于角色的访问控制RBAC&#xff1a;主要元素 OAuth2&#xff1a;面向第三方应用的认证授权协议业务场景OAuth2的工作流程OAuth2 四种不同的授权方式授权码模式&#xff08;A…

Android Studio 2024最新版Hello World

Android Studio 2024最新版Hello World 1. Android Studio 2024安装视频2. 创建项目Read Timed out 问题Android Studio Build Output 控制台中文乱码问题 3. 驱动管理 本文章介绍如何通过Android Studio 2024最新版创建项目&#xff0c; 并成功输出Hello World。 本次教程版本…

关于QT中使用网络编程QtNetwork的问题

在此处添加network模块 在链接器中添加附加库目录(QT对应的lib)在链接器-输入中添加对应的lib库(Qt5Network.lib) 这样,就可以使用对应的下面的库文件了,比如: #include <qnetworkaccessmanager.h>

参数高效微调(PEFT)综述

人工智能咨询培训老师叶梓 转载标明出处 大模型如BERT和GPT-3的参数数量庞大&#xff0c;动辄数十亿甚至数千亿级别&#xff0c;给进一步的微调和应用带来了巨大的挑战。针对这一问题&#xff0c;Vladislav Lialin、Vijeta Deshpande、Anna Rumshisky等研究者中提出了一系列参…

Leetcode面试经典150题-69.X的平方根

相当简单的题目&#xff0c;但是出现的概率还挺高的 解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public int mySqrt(int x) {/**0的平方根是0 */if(x 0) {return 0;}/**1~3的平方根是1 */if(x < 3) {return 1;}/**其他情况我们采用二分查找&#xff…

Python 调用手机摄像头

Python 调用手机摄像头 在手机上安装软件 这里以安卓手机作为演示&#xff0c;ISO也是差不多的 软件下载地址 注意&#xff1a;要想在电脑上查看手机摄像头拍摄的内容的在一个局域网里面(没有 WIFI 可以使用 热点 ) 安装完打开IP摄像头服务器 点击分享查看IP 查看局域网的I…

Android Studio下载Gradle失败问题解决

问题说明 使用 Android Studio 构建程序报错如下 Could not install Gradle distribution from https://services.gradle.org/distributions/gradle-7.5.1-bin.zip. Reason: java.net.SocketTimeoutException: Connect timed out问题解决 下载对应版本的压缩包 gradle-7.5.1…

香橙派转换模型以及在开发板上部署

新手小白记录一下自己使用香橙派模型转换以及在开发板上运行的过程&#xff0c;防止后面忘记。 使用的开发板&#xff1a;Orange Pi 5 Plus&#xff08;rk3588&#xff09; 官方的一些资料在&#xff08;主要参考用户手册&#xff09;&#xff1a;Orange Pi - Orangepihttp:/…