C++(13)——STl之List的实现

2023-11-04

STL

STL是C++的标准模板库,是一个具有工业强度,高效的C++程序库。

  • STL一个最为重要的特点就是数据结构和算法的分离,你可以使用其中的一些函数操作几乎热河的数据集合,包含链表,容器和数组。
  • STL的另一个特性就是它不是面向对象的,STL主要依赖模板而不是封装,继承和虚函数(多态性)

STL中包含的六大组件

  1. 容器(container):包含list、vector、deques,以模板类的方法提供,为了访问容器中的数据,可以使用由容器类输出的迭代器。
  2. 迭代器(iterator):提供为了访问容器中对象的方法。
  3. 算法(Algorithm):是用来操作容器中的数据的模板函数。
  4. 仿函数(Functor)
  5. 适配器(Adaptor)
  6. 分配器(allocator)

大致了解STL是什么之后,完成对list类的理解,仿照VC6.0中list的源码,我们来自己实现一下list。
相关的细节我们已经在注释中写出:

#ifndef MK_LIST_H
#define MK_LIST_H
#include<iostream>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#include<initializer_list>
using namespace std;

namespace mk
{
template<class T>
void Swap(T &a,T&b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
template<class _Ty>
class list
{
protected:
	struct _Node;
	typedef struct _Node* _Nodeptr;
	struct _Node
	{
		_Nodeptr _Prev, _Next;
		_Ty _Value;
	};
	struct _Acc;
	struct _Acc
	{
		typedef struct _Node*& _Nodepref;
		typedef _Ty& _Vref;
		//三个静态函数用于服务_Node结点的一些操作
		static _Vref _Value(_Nodeptr _P)//值引用返回
		{
			return (*_P)._Value;
		}
		static _Nodepref _Prev(_Nodeptr _P)//值结点指针引用返回
		{
			return (*_P)._Prev;
		}
		static _Nodepref _Next(_Nodeptr _P)//值结点指针引用返回
		{
			return (*_P)._Next; // return _P->_Next;
		}
	};
public:
	typedef _Ty          value_type;
	typedef _Ty&         reference;
	typedef const _Ty&   const_reference;
	typedef _Ty*         pointer;
	typedef const _Ty*   const_pointer;

	typedef size_t       size_type;
	typedef int          difference_type;

public:
	class const_iterator;
	class iterator;

	class const_iterator//对链表的数据值只能读不能写
	{
	protected:
		_Nodeptr _Ptr;//指向一个节点的首地址
	public:
		const_iterator(_Nodeptr _P = NULL) :_Ptr(_P) {}
		const_reference operator*() const
		{
			return _Acc::_Value(_Ptr);
		}
		const_pointer operator->() const
		{
			return &**this;
		}
		const_iterator & operator++()
		{
			_Ptr = _Acc::_Next(_Ptr);
			return *this;
		}
		const_iterator operator++(int)
		{
			const_iterator tmp = *this;
			++* this;
			return tmp;
		}
		const_iterator& operator--()
		{
			_Ptr = _Acc::_Prev(_Ptr);
			return *this;
		}
		const_iterator operator--(int)
		{
			const_iterator tmp = *this;
			--* this;
			return tmp;
		}
		bool operator==(const const_iterator& _X) const
		{
			return this->_Ptr == _X._Ptr;
		}
		bool operator!=(const const_iterator& _X) const
		{
			return !(*this == _X);
		}

		_Nodeptr _Mynode() const
		{
			return _Ptr;
		}
	};
	//普通迭代器,在List内部进行声明
	class iterator:public const_iterator//继承常性迭代器(公有继承:是一个)
	{
		typedef const_iterator base;
		// reference operator*() const//子类型和父类型的方法名相同:同名隐藏(不会调动父类里的方法,将其隐藏)
		// {
		// 	return _Acc::_Value(_Ptr);
		// }
		// pointer operator->() const
		// {
		// 	return &**this;
		// }
		
	public:
		iterator(_Nodeptr _P = NULL) :const_iterator(_P) {}
		iterator& operator++()
		{
			base::_Ptr = _Acc::_Next(base::_Ptr);
			return *this;
		}
		iterator operator++(int)
		{
			iterator tmp = *this;
			++* this;
			return tmp;
		}
		iterator& operator--()
		{
			base::_Ptr = _Acc::_Prev(base::_Ptr);
			return *this;
		}
		iterator operator--(int)
		{
			iterator tmp = *this;
			--* this;
			return tmp;
		}
		bool operator==(const iterator& _X) const
		{
			return this->_Ptr == _X._Ptr;
		}
		bool operator !=(const iterator& _X) const
		{
			return !(*this == _X);
		}
	};
public:
	iterator begin()
	{
		return iterator(_Acc::_Next(_Head));
	}
	iterator end() 
	{
		return iterator(_Head);
	}
	const_iterator begin() const 
	{
		return const_iterator(_Acc::_Next(_Head));
	}
	const_iterator end() const 
	{
		return const_iterator(_Head);
	}

public:
	typedef  const_iterator _It;
	//使用系统提供的初始化列表
	list(std::initializer_list<_Ty> list):list()
	{
		for(auto &x:list)
		{
			push_back(x);
		}
	}
	list() :_Head(_Buynode()), _Size(0) {}
	list(size_t count, const _Ty& val):_Head(_Buynode()), _Size(0)
	{
		insert(begin(), count, val);
	}
	list(const _Ty* _F, const _Ty* _L) :_Head(_Buynode()), _Size(0)
	{
		insert(begin(), _F, _L);
	}
	list(const list& _X) :_Head(_Buynode()), _Size(0)
	{
		insert(begin(), _X.begin(), _X.end());
	}
	list& operator=(const list _X)
	{
		if(this == &_X)return *this;
		iterator _F1 = begin();
		iterator _L1 = end();
		const_iterator _F2 = _X.begin();
		const_iterator _L2 = _X.end();

		for(;_F1 != _L1 && _F2 != _L2;++_F1,++_F2)
		{
			*_F1 = *F2;		
		}
		earse(_F1,_L1);
		insert(_L1,_F2,_L2);
		return *this;
	}
	~list()
	{
		clear();
		_Freenode(_Head);
	}
	void push_front(const _Ty& val)
	{
		insert(begin(), val);
	}
	void push_back(const _Ty& val)
	{
		insert(end(), val);
	}
	void insert(iterator _P,const _Ty *_F,const _Ty *_L)//插入一个数组
	{
		for (; _F != _L; ++_F)
		{
			insert(_P, *_F);
		}
	}
	void insert(iterator _P, size_t count,const _Ty &val)//插入同样的数值count个
	{
		while (count--)
		{
			insert(_P, val);
		}
	}
	void insert(iterator _P, _It _F, _It _L)//把一个链表插入到另一个链表
	{
		for (; _F != _L; ++_F)
		{
			insert(_P, *_F);
		}
	}
	void insert(iterator _P,std::initializer_list<_Ty> list)
	for(auto &x : List)
	{
		insert(_P,x);
	}
	iterator insert(iterator _P, const _Ty& val)//当前迭代器之前插入
	{
		_Nodeptr _S = _P._Mynode();//_S指向当前迭代器
		_Acc::_Prev(_S) = _Buynode(_Acc::_Prev(_S), _S);//购买节点,完成三步操作
		_S = _Acc::_Prev(_S);//_S指向插入节点
		_Acc::_Next(_Acc::_Prev(_S)) = _S;//完成最后一步操作
		new(&_Acc::_Value(_S)) _Ty(val);//定位new
		_Size += 1;
		return iterator(_S);
	}
	void pop_front()
	{
		erase(begin());
	}
	void pop_back()
	{
		erase(--end());//不是头节点,--指向最后一个节点
	}
	void erase(iterator _F, iterator _L)
	{
		for (; _F != _L; )
		{
			erase(_F++);
		}
	}
	void clear()
	{
		erase(begin(), end());
	}
	void remove(const _Ty &val)//从左往右遍历,删除第一个与val相等的结点
	{
		iterator _F = begin(),_L = end();
		while(_F != _L)
		{
			if(*_F != val)
			{
				_flag++;
			}
			else
			{
				erase(_F);
				break;  
			}
		}
	}
	void remove_all(const _Ty &val)//删除所有与val相等的结点
	{
		iterator _F = begin(),_L = end();
		while(_F != _L;)
		{
			if(*_F == val)
			{
				erase(_F++);
			}
		}
	}
	iterator erase(iterator _P)//返回删除结点后续一个结点迭代器的地址
	{
		_Nodeptr _node_del = _P++._Mynode();//调动顺序虽然如下:++ ——>_Mynode
		//此处为后置++,但是将亡迭代器tmp调动了_Mynode,即删除结点调动了_Mynode,因此是正确的的写法
		_Acc::_Next(_Acc::_Prev(_node_del)) = _Acc::_Next(_node_del);
		_Acc::_Prev(_Acc::_Next(_node_del)) = _Acc::_Prev(_node_del);
		(&_Acc::_Value(_node_del))->~_Ty();//对象释放自身资源(自定义类型),取value的原因就是需要释放的不是结点,而是结点内的对象
		_Freenode(_node_del);//释放空间,无法调动析构
		this->_Size--;
		return _P;//注意失效迭代器问题
	}
	
	void Swap(List& _X)
	{
		mk::Swap(this->_Head,_X._Head);
		mk::Swap(this->_Size,_X._Size);
	}
private:
	_Nodeptr _Buynode(_Nodeptr _Parg = NULL, _Nodeptr _Narg = NULL)
	{
		_Nodeptr _S = (_Nodeptr)malloc(sizeof(_Node));
		_Acc::_Prev(_S) = _Parg == NULL ? _S : _Parg;
		_Acc::_Next(_S) = _Narg == NULL ? _S : _Narg;
		return _S;
	}
	void _Freenode(_Nodeptr _P)
	{
		free(_P);
	}
	_Nodeptr _Head;
	size_type _Size;//数据结点的个数,不包含头结点
};
}
#endif

就地构造问题

使产生对象的个数变少,比如下面的代码:

 std::list<Object> alist;
 alist.push_back(Object(23));//2
 Object a(10;)alist.push_back(Object(a));//3
 alist.emplace_back(12);//4
  • 第二行代码:按照push_back构建,先产生一个无名对象(右值),插入节点之后,将右值对象的资源移动构造到已插入结点中,将无名对象的资源拿到手,一共产生两个对象
  • 第三行代码:按照push_back构建,它是一个具名对象(左值),此时再按照拷贝构造的方式进行构造,此时不会使用移动构造,因为一旦使用移动构造,a所拥有的资源将不存在,构建完成之后,需要再使用a时,此时它已经不具有资源,这是不被允许的,除非明确告知a不再需要使用此资源:alist.push_back(std::move(a));,强转成右值,形成上面的调动方式,我们需要考虑是否需要使用到a的资源,如何有效地利用资源,一共产生两个对象
  • 第四行代码:按照emplace_back构建,我们不深究其实现方式(源码过于复杂),只了解这个过程中只产生一个对象即可。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++(13)——STl之List的实现 的相关文章

  • 分段错误(核心转储)错误

    我的程序编译罚款 但在输入文件时出现 分段错误 核心转储 错误 我没有正确处理 ostream 吗 include
  • ASP.NET Core 与现有的 IoC 容器和环境?

    我想运行ASP NET 核心网络堆栈以及MVC在已托管现有应用程序的 Windows 服务环境中 以便为其提供前端 该应用程序使用 Autofac 来处理 DI 问题 这很好 因为它已经有一个扩展Microsoft Extensions D
  • C# 正则表达式用于查找 中具有特定结尾的链接

    我需要一个正则表达式模式来查找字符串 带有 HTML 代码 中的链接 以获取文件结尾如 gif 或 png 的链接 示例字符串 a href site com folder picture png target blank picture
  • 在 C# Winforms 应用程序中嵌入 Windows XP 主题

    我有一个旧版 C Windows 窗体应用程序 其布局是根据 Windows XP 默认主题设计的 由于需要将其作为 Citrix 应用程序进行分发 该应用程序现在看起来像经典主题应用程序 因为 Citrix 不鼓励使用主题系统服务 所以
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 将字符串转换为正确的 URI 格式?

    有没有简单的方法可以将电子邮件地址字符串转换为正确的 URI 格式 Input http mywebsite com validate email 3DE4ED727750215D957F8A1E4B117C38E7250C33 email
  • 带 If 的嵌套 For 循环的时间复杂度

    void f int n for int i 1 i lt n i if i int sqrt n 0 for int k 0 k lt pow i 3 k do something 我的思考过程 执行if语句的次数 sum i 1 to
  • 劫持系统调用

    我正在编写一个内核模块 我需要劫持 包装一些系统调用 我正在暴力破解 sys call table 地址 并使用 cr0 来禁用 启用页面保护 到目前为止一切顺利 一旦完成 我将公开整个代码 因此如果有人愿意 我可以更新这个问题 无论如何
  • C# 委托责任链

    为了我的理解目的 我实现了责任链模式 Abstract Base Type public abstract class CustomerServiceDesk protected CustomerServiceDesk nextHandle
  • OpenCV 2.4.3 中的阴影去除

    我正在使用 OpenCV 2 4 3 最新版本 使用内置的视频流检测前景GMG http docs opencv org modules gpu doc video html highlight gmg gpu 3a 3aGMG GPU算法
  • 为什么具有相同名称但不同签名的多个继承函数不会被视为重载函数?

    以下代码片段在编译期间产生 对 foo 的调用不明确 错误 我想知道是否有任何方法可以解决此问题而不完全限定对 foo 的调用 include
  • 默认析构函数做了多少事情

    C 类中的默认析构函数是否会自动删除代码中未显式分配的成员 例如 class C public C int arr 100 int main void C myC new C delete myC return 0 删除 myC 会自动释放
  • MySQL #1093 - 您无法在 FROM 子句中指定用于更新的目标表“赠品”

    I tried UPDATE giveaways SET winner 1 WHERE ID SELECT MAX ID FROM giveaways 但它给出了 1093 您无法指定目标表 赠品 进行更新FROM clause 本文 ht
  • 从 R 到 C 处理列表并访问它

    我想使用从 R 获得的 C 列表 我意识到这个问题与此非常相似 使用 call 在 R 和 C 之间传递数据帧 https stackoverflow com questions 6658168 passing a data frame f
  • WPF。如何从另一个窗口隐藏/显示主窗口

    我有两个窗口 MainWindow 和 Login 显示登录的按钮位于主窗口 this Hide Login li new Login li Show 登录窗口上有一个检查密码的按钮 如果密码正确 我如何显示主窗口 将参数传递给 MainW
  • ASP.NET JQuery AJAX POST 返回数据,但在 401 响应内

    我的应用程序中有一个网页 需要调用我设置的 Web 服务来返回对象列表 这个调用是这样设置的 document ready function var response ajax type POST contentType applicati
  • 初始化 LPCTSTR /LPCWSTR [重复]

    这个问题在这里已经有答案了 我很难理解并使其正常工作 基本上归结为我无法成功初始化这种类型的变量 它需要有说的内容7 2E25DC9D 0 USB003 有人可以解释 展示这种类型的正确初始化和类似的值吗 我已查看此站点上的所有帮助 将项目
  • 在 Xamarin 中获取 OutOfMemoryException

    java lang OutOfMemoryError 考虑增加 JavaMaximumHeapSize Java 执行时内存不足 java exe 我的 Visualstudio Xamarin 项目出现内存不足异常 请帮助我如何解决此问题
  • 以 UTF8 而不是 UTF16 输出 DataTable XML

    我有一个 DataTable 我正在使用 WriteXML 创建一个 XML 文件 尽管我在以 UTF 16 编码导出它时遇到问题 并且似乎没有明显的方法来更改它 我了解 NET 在字符串内部使用 UTF 16 这是正确的吗 然后 我通过
  • 如何使用 C# 以低分辨率形式提供高分辨率图像

    尝试使用 300dpi tif 图像在网络上显示 目前 当用户上传图像时 我正在动态创建缩略图 如果创建的页面引用宽度为 500x500px 的高分辨率图像 我可以使用相同的功能即时转换为 gif jpg 吗 将创建的 jpg 的即将分辨率

随机推荐

  • 毕业设计-基于 MATLAB的红外图像预处理算法对比研究

    目录 前言 课题背景和意义 实现技术思路 实现效果图样例 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生级别难度
  • codeforces - 920B(贪心)

    B Tea Queue time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output
  • 在没有自己的服务器的情况下,搭建自己的网站-开发自己的小程序接口

    目标 场景一 在没有自己的服务器的情况下 搭建自己的网站 并通过微信小程序给自己的网站引流 场景二 在没有自己的服务器的情况下 开发自己的小程序 后端接口使用第三方网站的某些功能完成小程序前后端数据交互能力 实现 场景一 使用gitee的g
  • 分贝dB的换算

    1dB 0 115Np 公式 公式 d B 10 l g
  • adworld.xctf-web-新手练习区刷题

    1 View Source 根据题目 就能猜到 此处是查看页面源码 在查看的过程中发现右键不能使用了 那就用F12吧 2 Robots 这题是考察Robots协议 访问的时候页面是一片空白 直接输入robots txt 在地址栏输入robo
  • 微信小程序滚动Tab选项卡:左右可滑动切换

    最终效果如上 问题 1 tab标题总共8个 所以一屏无法全部显示 2 tab内容区左右滑动切换时 tab标题随即做标记 active 3 当active的标题不在当前屏显示时 要使其能显示到当前屏中 一 wxml结构 tab标题因一排八个
  • Redis学习笔记(持续更新中...)

    学习课程 尚硅谷Redis6 目录 NoSQL简介 NoSQL概述 NoSQL的特点 适用场景 NoSQL不适用场景 几种常见的NoSQL数据库 Memcache Redis MongoDB Redis Redis的底层 Redis的五大数
  • 获取扫描枪数据并存入TXT文档的C#程序代码

    我可以提供一段 C 语言代码 用于获取扫描枪数据并存入TXT文档 include
  • 数仓分层、设计、建模、架构

    一 数仓分层误区 数仓层内部的划分不是为了分层而分层 分层是为了解决 ETL 任务及工作流的组织 数据的流向 读写权限的控制 不同需求的满足等各类问题 业界较为通行的做法将整个数仓层又划分成了 DWD DWT DWS DIM DM等很多层
  • facenet专题1:windows下使用train_sotfmax.py训练人脸识别模型

    1 facenet github地址 https github com davidsandberg facenet 下载该project git clone https github com davidsandberg facenet 2
  • node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

    VScode Code Runner无法运行JavaScript js文件 原因 未安装Node js 解决方法 https nodejs org en 下载Nodejs 安装完之后 重启VScode 会自动配置 运行即可在终端看到结果
  • 【tauri】tauri的启动、运行与打包过程步骤

    兼容性 win11 系统 自带 WebView2 win10 安装完会自动安装WebView2 win7 需要先手动来装WebView2 开始安装 安装node 推荐用nvm将进行node版本管理 安装vs 生成工具 下载地址 https
  • 单片机 嵌入式 毕业设计题目选题推荐

    文章目录 1前言 2 如何选题 2 1 不要给自己挖坑 2 2 难度把控 2 3 如何命名题目 3 单片机 嵌入式 选题大全 3 1 嵌入式方向 3 2 算法方向 3 3 移动通信方向 3 4 学长作品展示 4 最后 1前言 近期不少学弟学
  • 子类的构造函数和析构函数

    1 构造函数是否可以被继承 子类可以继承父类的所有成员变量和成员函数 但不能继承父类的构造函数 因此 在创建子类对象时 为了初始化从父类继承来的数据成员 系统需要调用其父类的额构造函数 2 父类构造函数的调用规则 如果子类没有定义构造函数
  • axios的三层封装思想

    1 工具函数层 设置默认请求地址 设置默认超时时间 设置默认请求拦截 设置默认响应拦截 ajax工具函数层 import axios from axios axios defaults baseURL http localhost 5000
  • vue2插件开发小试

    开发vue插件的官方文档是这样描述的 插件通常会为Vue添加全局功能 插件的范围没有限制 一般有下面几种 1 添加全局方法或者属性 如 vue element 2 添加全局资源 指令 过滤器 过渡等 如 vue touch 3 通过全局 m
  • KDD 2023

    下载地址 点我跳转 1 DoubleAdapt A Meta learning Approach to Incremental Learning for Stock Trend Forecasting Code None Area 一种用于
  • ubuntu 安装Pangolin 过程

    前言 大家好 好久没有写技术博客了 在工作学习中遇到一些问题及解决方法 希望能帮助到大家 Pangolin 想必大家都非常熟悉了 这个是一款开源的OPENGL显示库 可以用来视频显示 而且开发容易 代码我们可以从Github 进行下载 ht
  • JSP基础语法

    1 gt 2 gt hr
  • C++(13)——STl之List的实现

    STL STL是C 的标准模板库 是一个具有工业强度 高效的C 程序库 STL一个最为重要的特点就是数据结构和算法的分离 你可以使用其中的一些函数操作几乎热河的数据集合 包含链表 容器和数组 STL的另一个特性就是它不是面向对象的 STL主