C++常见容器实现原理

news2025/1/31 10:41:41

引言

  • 如果有一天!你骄傲离去!(抱歉搞错了)
  • 如果有一天,你在简历上写下了这段话:
    在这里插入图片描述
  • 那么你不得不在面试前实现一下STL常见的容器了。
  • C++的常用容器有:vector、string、deque、stack、queue、list、set、map。接下来就让我们对每种常用容器进行介绍和实现吧。

一、vector

  • vector详细介绍
  • 实现代码:
#include<assert.h>
#include<algorithm> // 包含函数std::swap

// 模拟实现Vector
template<class T>	// T为容器中元素的类型
class vector
{
public:
	typedef T* iterator;			  // 统一化指向vector中元素的指针为:iterator
	typedef const T* const_iterator;  // const_iterator指针无法修改指向的对象,但可以自增自减

	// 获取迭代器(const和非const)
	iterator begin()
	{
		return _start;	  // _start指向容器内存储的第一个元素
	}

	iterator end()
	{
		return _finish;	  // _finish指向容器内最后一个元素之后
	}

	const_iterator cbegin()const	// 实现同上即可,返回的类型是const_iterator
	{
		return _start;
	}

	const_iterator cend()const
	{
		return _finish;
	}

	// 运算符[]的重载
	T& operator[](size_t pos)
	{
		assert(pos < size());	// size()返回容器内容纳的元素个数
		return _start[pos];		// 根据指针运算,直接索引到Pos索引值即可
	}

	const T& operator[](size_t pos)	// 同上
	{
		assert(pos < size());
		return start[pos];
	}

	// 构造函数
	vector() :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {}	// 初始化三个成员指针为空指针

	// 迭代器区间构造函数
	template<class InputIterator>
	vector(InputIterator first, InputIterator last) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
	{
		while (first != last)	// 将区间中的每个元素按照顺序压入容器内即可
		{
			push_back(*first);
			first++;
		}
	}

	// 拷贝构造函数
	vector(const vector<T>& v) :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
	{
		vector<T> tmp(v.cbegin(), v.cend());	// 先使用区间构造一个同样的容器,然后把其和此容器进行交换即可
		swap(tmp);
	}

	// 使用n个val值的构造函数
	vector(size_t n, const T& val = T())
	{
		reserve(n);						// 将容器扩容到至少n个元素
		for (size_t i = 0; i < n; ++i)	// 将值压入即可
		{
			push_back(val);
		}
	}

	// 重载运算符=
	vector<T>& operator=(vector<T> v)
	{
		swap(v);		// 由于v不是引用,是新构造来的,直接交换控制权即可
		return *this;	// 返回自身
	}

	// 析构函数
	~vector()			
	{
		delete[] _start;	// 释放申请的空间
		_start = _finish = _endOfStorage = nullptr;		// 将所有成员指针重置为空
	}

	// 容量调整函数(只扩容)
	void reserve(size_t n)
	{
		if (n > capacity())	// 当且仅当需求的容量大于现在容器的最大容量时进行调整
		{
			size_t oldSize = size();	// 获取原本的容量
			T* tmp = new T[n];			// 申请需求的更大容量
			if (_start != nullptr)		// 如果容器本身中包含元素
			{
				for (int i = 0; i < size(); ++i)	// 将本身包含的元素拷贝到新申请的容量中
				{
					tmp[i] = _start[i];
				}
				delete[] _start;		// 释放原本的内存空间
			}
			_start = tmp;				// 将此容器的首地址记录为新申请的空间
			_finish = tmp + oldSize;	// 计算容器中最后一个元素之后的地址
			_endOfStorage = _start + n; // 计算容器中最大容量元素之后的地址
		}
	}

	// 元素个数调整函数(只扩容)
	void reserve(size_t n, T val = T())
	{
		if (n > capacity())	// 如果需要扩容则进行扩容
		{
			reserve(n);
		}
		if (n > size())		// 如果容器包含元素数目少于n,则将包含元素数目-n中的元素设为val
		{
			while (_finish < _start + n)
			{
				*_finish = val;
				_finish++;
			}
		}
		else
		{					// 否则容器包含元素数目多于n,则将容器包含元素数目设为n
			_finish = _start + n;
		}
	}

	// 获取元素个数size
	size_t size()const
	{
		return _finish - _start;	// 指针运算
	}

	// 获取容量大小
	size_t capacity()const
	{
		return _endOfStorage - _start;	// 指针运算
	}

	// 判断是否为空
	bool empty()const
	{
		return _finish == _start;	// 当首元素地址 等于 最后一个元素之后的地址 时,即不存在元素即为空
	}

	void clear()
	{
		_finish = _start;	// 清空,即最后一个元素之后的地址等于空间首地址
	}

	// 尾部插入函数
	void push_back(const T& x)
	{
		if (_finish == _endOfStorage)	// 如果空间满了
		{
			size_t newCapacity = (capacity() == 0 ? 4 : capacity() * 2);	// 扩容两倍
			reserve(newCapacity);	// 扩容
		}
		*_finish = x;	// 将最后一个之后的元素设为x,即压入x
		_finish++;		// 尾指针向后移动
	}

	// 尾部删除函数
	void pop_back()
	{
		assert(!empty());	// 不为空时删除
		_finish--;			// 直接将尾指针向前移动即可
	}

	// 插入指定位置
	void insert(iterator pos, const T& val)
	{
		assert(pos < _finish);	// 插入位置需要位于:[_start,finish)
		assert(pos >= _start);

		if (_finish == _endOfStorage) // 如果空间满了
		{
			size_t len = pos - _start;		//计算此位置之前有多少元素
			size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;	// 计算扩容后的容量数目
			reserve(newCapacity);			// 扩容

			pos = _start + len;	 // 计算新的对应pos位置 
		}

		iterator end = _finish - 1; // 计算尾元素位置
		while (end >= pos)		// 从尾元素到插入位置之后的位置
		{
			*(end + 1) = *end;	// 每个元素向后移动
			end--;
		}
		*pos = val;  // 在pos位置插入val
		_finish++;	 // 将finish+1 
	}

	// 删除指定位置
	iterator erase(iterator pos)
	{
		assert(pos >= _start);
		assert(pos < _finish);

		iterator begin = pos;	
		while (begin < _finish - 1)	 // 从删除位置到最后一个元素
		{
			*(begin) = *(begin + 1); // 每个元素向前移动一位
			begin++;
		}
		_finish--;	// 尾指针-1
		return pos;
	}

	// 交换
	void swap(vector<T>& v)	// 交换对应指针即可
	{
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_endOfStorage, v._endOfStorage);
	}

private:
	iterator _start;			// 容器中第一个元素地址,也是容器申请内存空间首地址
	iterator _finish;			// 容器中最后一个元素之后的地址
	iterator _endOfStorage;		// 容器申请的内存空间的尾地址,也即是容器能容纳的最多元素之后的地址
};

二、list

  • 实现代码:

三、map

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

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

相关文章

合宙ESP32C3之Arduino、MicroPython上手

此处所说的ESP32C3&#xff0c;是合宙9.9元包邮的那一款&#xff0c;即所谓的“简约款”&#xff0c;无串口芯片。虽然有串口芯片的经典款版本兼容性更好&#xff0c;但随着各种IDE的不断升级&#xff0c;无串口使用起来也能游刃有余。 1.Arduino环境搭建 首先到Arduino.cc上下…

利用MATLAB创建栅格地图(代码可复制)

先做一个声明&#xff1a;文章是由我的个人公众号中的推送直接复制粘贴而来&#xff0c;因此对智能优化算法感兴趣的朋友&#xff0c;可关注我的个人公众号&#xff1a;启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法&#xff0c;经典的&#xff0c;或者是近几年…

CoDeSys系列-3、Windows运行时软PLC主站和p-net从站IO设备组网测试

CoDeSys系列-3、Windows运行时软PLC主站和p-net从站IO设备组网测试 文章目录 CoDeSys系列-3、Windows运行时软PLC主站和p-net从站IO设备组网测试一、前言二、Windows运行时软plc配置编程1、安装Windows下的运行时扩展包&#xff08;非必要&#xff09;2、创建项目2.1、创建标准…

基于Java的新闻发布管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09; 代码参考数据库参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…

爬虫进阶-反爬破解8(反爬的实战练习:爬虫文件的解析和数据的抓取+反爬措施的分析和突破+Scrapy接入Cookie池管理系统+分布式爬虫的架设)

目录 一、爬虫文件的解析和数据的抓取 &#xff08;一&#xff09;项目的知识点 &#xff08;二&#xff09;实践操作&#xff1a;新建项目抓取数据 &#xff08;三&#xff09;总结 二、反爬措施的分析和突破 &#xff08;一&#xff09;项目知识点补充 &#xff08;二…

binutils 2.40 Linker (ld) 官方文档下载

前言 最近需要熟悉 elf 与 共享库 的链接与加载流程&#xff0c;需要先了解 elf 文件 是怎么链接的&#xff0c;链接脚本如何阅读 最有效的方式是查看 GNU 官方的 Linker (ld) 文档&#xff0c;通过查找&#xff0c;这个 Linker (ld) 属于 GNU binutils&#xff0c;当前的较新…

JS DataTable中导出PDF中文乱码

JS DataTable中导出PDF中文乱码 文章目录 JS DataTable中导出PDF中文乱码一. 问题二. 原因三. vfs_fonts.js四. pdfmake.js五. 解决六.参考资料 一. 问题 二. 原因 DataTable使用pdfmake&#xff0c;pdfmake默认字体为Roboto&#xff0c;不支持中文字体。添加自己的字体&#…

Shopee本土店与跨境店有何区别?如何入驻?

截止到目前&#xff0c;虾皮全球覆盖的站点已经有11个&#xff0c;其中东南亚站点依然是消费力强劲的大站点&#xff0c;包括马来西亚、泰国、印度尼西亚、菲律宾、越南、新加坡。到了2023&#xff0c;仍然有非常大的市场空间。 东南亚人口密度大&#xff0c;还是全球网络发展…

【项目管理】项目中如何进行风险管理

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

threejs(4)-纹理材质高级操作

一、纹理重复_缩放_旋转_位移操作 // 导入threejs import * as THREE from "three"; // 导入轨道控制器 import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; // 导入lil.gui import { GUI } from "three/examples/jsm/l…

基于5G网关的风力发电远程监测方案优势

风力发电是清洁能源的典型代表&#xff0c;是我国能源结构转型的重要组成。近年来我国大力发展风能、水能、光伏等清洁能源&#xff0c;加速双碳战略&#xff0c;长期致力于创造人与生态友好和谐的人居社会。 针对风力发电机组和厂区的运营和管理&#xff0c;5G技术、物联网技…

CrossOver23.6软件激活码怎么获取 CrossOver软件2023怎么激活

CrossOver一款类虚拟机&#xff0c;它的主要功能是在mac系统中安装windows应用程序。其工作原理是将exe格式的windows应用程序安装包安装至CrossOver容器中&#xff0c;并将运行该exe文件所需的配置文件下载至容器中&#xff0c;便能在mac正常运行windows应用程序了。下面就让我…

JavaSE介绍与第一个Java程序

JavaSE介绍与第一个Java程序 一、Java简介二、Java特点三、编译运行过程四、JDK、JRE和JVM的关系五、第一个Java程序1、HelloWorld2、注意事项 六、标识符与命名规范1、标识符&#xff08;1&#xff09;什么是标识符&#xff08;2&#xff09;标识符的命名规则 2、命名规范&…

C++初阶:C/C++内存管理

一.C/C内存分布 先来回顾一下C语言内存分区示意图如下&#xff1a; 代码区&#xff1a; 程序执行代码一般存放在代码区&#xff0c;字符串常量以及define定义的常量也可能存放在代码区。 常量区&#xff1a; 字符串&#xff0c;数字等常量以及const修饰的全局变量往往存放在…

【UE】UMG通信的三种方法

目录 前言 方法一&#xff1a;通过“获取类的所有控件”节点通信 方法二&#xff1a;当创建控件蓝图时传入其它控件蓝图的对象引用 *方法三&#xff1a;使用HUD类来管理UMG通信 前言 首先我们创建了三个控件蓝图&#xff0c;那么其中的一个控件蓝图如何与剩下的控件蓝图通…

网络编程进化史:Netty Channel 的崭新篇章

上篇文章&#xff08;Netty 入门 — ByteBuf&#xff0c;Netty 数据传输的载体&#xff09;&#xff0c;我们了解了 Netty 的数据是以 ByteBuf 为单位进行传输的&#xff0c;但是有了数据&#xff0c;你没有通道&#xff0c;数据是无法传输的&#xff0c;所以今天我们来熟悉 Ne…

2023 年专业人士必须尝试的 15 款人工智能工具

在本文中&#xff0c;我们将看到一些人工智能驱动的工具&#xff0c;这些工具将在 2023 年彻底改变开发并使专业人士的生活变得轻松。我们将讨论旨在为专业人士提供支持的 15 种顶级人工智能和低代码工具。人工智能工具现在变得更加强大&#xff0c;使他们能够创造有影响力的产…

数字图像处理(十六)非局部均值去噪

文章目录 一、前言二、NL-means1.两个邻域块的相似度2.NL-means原理3.数学理论推导4.代码链接 参考链接 一、前言 在之前我们已经介绍过许多图像去噪的方法&#xff0c;比如均值滤波、中值滤波、高斯滤波等。今天我们要介绍一种新的去噪方法——非局部均值去噪&#xff08;the…

如何构建一个外卖微信小程序

随着外卖行业的不断发展&#xff0c;越来越多的商家开始关注外卖微信小程序的开发。微信小程序具有使用方便、快速上线、用户覆盖广等优势&#xff0c;成为了商家们的首选。 那么&#xff0c;如何快速开发一个外卖微信小程序呢&#xff1f;下面就让我们来看看吧&#xff01; 首…