c++STL-list的模拟实现

c++STL-list的模拟实现

  • list源码剖析
  • list模拟实现
    • list构造函数
    • 拷贝构造函数
    • 赋值重载
    • 迭代器 iterator
    • 访问结点数size和判空
    • 尾插 push_back
    • 头插 push_front
    • 尾删pop_back
    • 头删pop_front
    • 插入 insert
    • 删除 erase
    • 清空clear和析构函数
    • 访问结点
  • 参考程序

list源码剖析

建议先看c++STL-list的使用和迭代器-CSDN博客。

STL中某版本的list的结点原型:

template <class T>
struct __list_node{typedef void* void_pointer;void_pointer next;voie_pointer prev;T data;
}

void 后期还需要强转。

定义结点用struct,可以换成class,但要另一个类将这个类封装成友元,因为class默认私有。

__list_node__表示内部的实现,算是项目上的约定。

之后list类的成员:

template <class T, class Alloc = alloc>
class list {
protected:typedef void* void_pointer;//结点类型typedef __list_node<T> list_node;//空间配置器typedef simple_alloc<list_node, Alloc> list_node_allocator;
public://类型typedef T value_type;//类型指针typedef value_type* pointer;typedef const value_type* const_pointer;//类型引用typedef value_type& reference;typedef const value_type& const_reference;typedef list_node* link_type;//结点数和引用数typedef size_t size_type;typedef ptrdiff_t difference_type;//迭代器typedef __list_iterator<T, T&, T*>             iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//...
protected:link_type node;
}

看代码(容器)先看构造函数,初始化决定初始结构是什么样的。

template<class T>
class list {list() { empty_initialize(); }//空结点初始化//证明链表在生成时会先//生成哨兵卫结点void empty_initialize() {node = get_node();node->next = node;node->prev = node;}link_type get_node() { return list_node_allocator::allocate(); }link_type create_node(const T& x) {link_type p = get_node();__STL_TRY{//调用定位newconstruct(&p->data, x);}__STL_UNWIND(put_node(p));return p;}void put_node(link_type p) { list_node_allocator::deallocate(p); }iterator begin() { return (link_type)((*node).next); }iterator end() { return node; }
};

allocate是空间配置器,也就是说get_node是获取一个新的结点。

list的迭代器功能比stringvector复杂,因此库里的list将迭代器封装成了一个类,通过模板参数的不同来区分不同类型的迭代器。

//Ref,即reference,引用
//Ptr,即pointer,指针
//表示引用类型和指针类型也作为模板参数的一部分
template<class T, class Ref, class Ptr>
struct __list_iterator {typedef __list_iterator<T, T&, T*>             iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_iterator<T, Ref, Ptr>           self;typedef bidirectional_iterator_tag iterator_category;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __list_node<T>* link_type;typedef size_t size_type;typedef ptrdiff_t difference_type;link_type node;__list_iterator(link_type x) : node(x) {}__list_iterator() {}__list_iterator(const iterator& x) : node(x.node) {}bool operator==(const self& x) const { return node == x.node; }bool operator!=(const self& x) const { return node != x.node; }reference operator*() const { return (*node).data; }#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */self& operator++() {node = (link_type)((*node).next);return *this;}self operator++(int) {self tmp = *this;++* this;return tmp;}self& operator--() {node = (link_type)((*node).prev);return *this;}self operator--(int) {self tmp = *this;--* this;return tmp;}
};

end()是尾结点的迭代器,begin()end()->next

list模拟实现

list的本质是封装加运算符重载。

因此list由三部分组成:

  1. 结点类__list_node
  2. 迭代器类__list_iterator
  3. 链表本体list

__list_node,考虑到库中的结点给的前驱和后缀都是void*,正式使用时还要强制转换,于是这里尝试做改进:

template<class T>
struct __list_node {//指针域typedef __list_node* pointer;pointer next;pointer view;//数据域T data;
};

为了能做到迭代器重载const修饰和非const修饰的迭代器,参考库中的__list_iterator,需要设计成三个模板参数的类模板,自己改进了看起来比较冗余的部分:

template<class T, class Ref, class Ptr>
struct __list_iterator {typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_iterator<T, Ref, Ptr> self;typedef __list_node<T>* link_node;link_node node;
};

最后是list本体:

class list {
public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_node<T> Node;//...
private:Node* node;
};

list构造函数

构造函数选择这几个常用的实现:

list ();list (size_t n, const T& val = T());template <class InputIterator>list (InputIterator first, InputIterator last);list (const list& x);

默认构造函数用于生成哨兵卫结点。

要使用list (InputIterator first, InputIterator last);,则需要再加上一个
list (int n, const T& val = T());,防止整型被识别成迭代器。

拷贝构造函数

同样可以创造新的头结点,并遍历链表(范围for)进行尾插。

赋值重载

  • 清理链表,保留头结点,之后遍历形参的链表即可。
  • 同样可以交换哨兵卫结点。

迭代器 iterator

迭代器用3个模板参数的模板类封装。

template<class T, class Ref, class Ptr>
struct __list_iterator {typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_iterator<T, Ref, Ptr> self;typedef __list_node<T>* link_node;link_node node;
};

迭代器需要支持的操作:

  1. 构造能指向指定结点的构造函数。
  2. 拷贝构造函数。
  3. 解引用操作*,返回迭代器指向的结点的数据域。要求数据域能读、能写。
  4. 解引用操作->,返回迭代器指向的结点的数据域,当数据域也是自定义类型时,返回指针。
  5. ==,用于判断两个迭代器指向的结点是否相等。
  6. !=,用于判断两个迭代器指向的结点是否不等。
  7. ++,当前迭代器指向后继结点。
  8. --,当前迭代器指向前驱结点。
  9. begin(),返回哨兵卫结点的后一个结点。
  10. end(),返回哨兵卫结点。

访问结点数size和判空

返回除哨兵卫结点外的结点数_size

判断_size是否为0,或哨兵卫的两个指针是否都指向自己。

尾插 push_back

void push_back(const T& val);

end()前插入结点即可。可以另外实现,也可以复用insert

头插 push_front

void push_front(const T& val);

begin()前插入结点即可。可以另外实现,也可以复用insert

尾删pop_back

删除--end()结点即可。

头删pop_front

删除begin()结点即可。

插入 insert

iterator insert(iterator pos, const T& val);

在迭代器pos前插入以val为数据的新结点。
请添加图片描述

这样在end()前插入结点相当于是尾插,在begin()前插入结点相当于是头插。

链表的迭代器不受扩容的影响,不会出现迭代器失效的问题,给不给返回值都可以。这里选择给。

删除 erase

iterator erase(iterator pos);

删除pos迭代器指向的结点。

删除后pos迭代器必定失效,因此需要返回下一个结点的迭代器。

清空clear和析构函数

void clear();

删除除哨兵卫结点外的所有结点。

析构函数在clear的基础上,进一步清理哨兵卫结点。

访问结点

用一个队首front和队尾back访问头、尾结点即可。

需要注意end()在这里设定为哨兵卫结点,一般情况下不可访问。

参考程序

某.h文件:

#pragma once
#include<cassert>namespace mystd {template<class Type>void swap(Type& a, Type& b) {Type tmp = a;a = b;b = tmp;}template<class T>struct __list_node {//指针域typedef __list_node* pointer;pointer next;pointer view;//数据域T data;__list_node(const T& x = T()):data(x), next(nullptr), view(nullptr) {}};//三个模板参数分别为:存储的数据类型//存储的数据的引用、存储的数据空间的地址类型template<class T, class Ref, class Ptr>struct __list_iterator {typedef __list_iterator<T, Ref, Ptr> self;typedef __list_node<T>* link_node;link_node node;__list_iterator<T, Ref, Ptr>(link_node x = nullptr):node(x) {}__list_iterator<T, Ref, Ptr>(const self& x): node(x.node) {}Ref operator*() {return node->data;}//为了支持T为自定义类型的情况//返回迭代器指向的结点的数据域的地址Ptr operator->() {return &node->data;}bool operator==(const self& x) const{return node == x.node;}bool operator!=(const self& x) const {return node != x.node;}self& operator++() {node = node->next;return *this;}self& operator--() {node = node->view;return *this;}self operator++(int) {self tmp(*this);node = node->next;return tmp;}self operator--(int) {self tmp(*this);node = node->view;return tmp;}};template<class T>class list {public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_node<T> Node;//默认构造函数list<T>() {node = get_node();node->next = node->view = node;_size = 0;}//构造函数list<T>(int n, const T& val = T()) {node = get_node();node->next = node->view = node;size_t tmp = n;for (size_t i = 0; i < tmp; i++)push_back(val);}list<T>(size_t n, const T& val = T()) {node = get_node();node->next = node->view = node;size_t tmp = n;for (size_t i = 0; i < tmp; i++)push_back(val);}template<class Inputiterator>list<T>(Inputiterator first, Inputiterator second) {node = get_node();node->next = node->view = node;while (first != second) {push_back(*first);first++;}}//拷贝构造函数list<T>(const list<T>& obj) {node = get_node();node->next = node->view = node;for (const auto& x : obj)this->push_back(x);}//赋值重载list<T>& operator=(list<T>obj) {mystd::swap(this->node, obj.node);mystd::swap(this->_size, obj._size);return *this;}//析构函数~list() {clear();delete node;}//迭代器iterator begin() {return iterator(node->next);}iterator end() {return iterator(node);}const_iterator begin() const {return const_iterator(node->next);}const_iterator end() const {return const_iterator(node);}//结点数size_t size()const {return _size;}//判断是否为空bool empty()const {return this->node->next == this->node&& this->node->view == this->node;}//头插void push_front(const T& val) {insert(begin(), val);}//尾插void push_back(const T& val) {insert(end(), val);}//尾删void pop_back() {erase(--end());}//头删void pop_front() {erase(begin());}//插入iterator insert(iterator pos, const T& val) {Node* cur = pos.node->view;Node* newnode = get_node(val);newnode->next = cur->next;newnode->view = cur;cur->next = newnode;newnode->next->view = newnode;++_size;return iterator(newnode);}//删除iterator erase(iterator pos) {assert(pos != end());Node* del = pos.node, * cur = pos.node->next;del->view->next = del->next;del->next->view = del->view;delete del;--_size;return iterator(cur);}//清空void clear() {auto it = begin();while (it != end()) {it = erase(it);}}//访问T& front() {return node->next->data;}T& back() {return node->view->data;}private:Node* get_node(const T& x = T()) {Node* tmp = new Node(x);tmp->next = tmp->view = nullptr;//这里建议赋值tmpreturn tmp;}template<class Type>friend void mystd::swap(Type&, Type&);Node* node;//哨兵卫size_t _size;//结点数};
}

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

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

相关文章

WeakAuras Lua Script ICC (BarneyICC)

WeakAuras Lua Script ICC &#xff08;BarneyICC&#xff09; https://wago.io/BarneyICC/69 全量英文字符串&#xff1a; !WA:2!S33c4TXX5bQv0kobjnnMowYw2YAnDKmPnjnb4ljzl7sqcscl(YaG6HvCbxaSG7AcU76Dxis6uLlHNBIAtBtRCVM00Rnj8Y1M426ZH9XDxstsRDR)UMVCTt0DTzVhTjNASIDAU…

校园网规划与设计方案

一、项目概述 校园网是学校实现信息化教学、科研与管理的重要基础设施,其性能与稳定性直接影响学校的整体发展。随着学校规模不断扩大、教学科研活动日益丰富,对校园网的带宽、可靠性、安全性以及智能化管理等方面提出了更高要求。本规划与设计方案旨在构建一个高速、稳定、…

算法分析:蛮力法

一、实验目的 1 掌握蛮力法的设计思想(利用计算机去穷举所有的可能解,再从中依次找出可行解) 2 掌握蛮力法的具体实现和时间复杂度分析 3 理解蛮力法的常见特性 实验要求&#xff1a;先用伪代码描述利用蛮力法解决的算法解决方案&#xff0c;再用程序实现&#xff0c;计算时间…

信息系统运行管理员:临阵磨枪版

信息系统运行管理员考试 - 全覆盖详细背诵大纲 (根据考情分析和原始材料&#xff0c;力求完整覆盖考点细节) 第一部分&#xff1a;基础知识与运维概览 Chapter 1: 信息系统运维概述 (上午题 5分) 信息&#xff1a; 含义&#xff1a;香农 - 减少随机不确定性的东西&#xff1b…

Linux的进程管理和用户管理

gcc与g的区别 比如有两个文件&#xff1a;main.c mainc.cpp&#xff08;分别是用C语言和C语言写的&#xff09;如果要用gcc编译&#xff1a; gcc -o mainc main.c gcc -o mainc mainc.cpp -lstdc表明使用C标准库&#xff1b; 区别一&#xff1a; gcc默认只链接C库&#x…

Python 常用模块(八):logging模块

目录 一、引言&#xff1a;日志模块在项目开发中的重要性二、从 Django 日志配置看 Logging 模块的核心组成三、logging模块核心组件详解3.1 记录器Logger3.2 级别Level3.3 根记录器使用3.4 处理器Handler3.5 格式化器Formatter3.6 日志流3.7 日志示例 四、日志模块总结 一、引…

Servlet原理

Servlet 体系结构的类层次关系 Servlet&#xff08;接口&#xff09;&#xff1a;定义了 Servlet 的核心生命周期方法&#xff08;如 init()、service()、destroy()&#xff09;&#xff0c;是所有 Servlet 的顶层规范&#xff0c;任何 Servlet 都需实现该接口。GenericServlet…

数据科学和机器学习的“看家兵器”——pandas模块 之五

目录 4.5 pandas 高级数据处理与分析 一、课程目标 二、对数据表格进行处理 (一)行列转置 (二)将数据表转换为树形结构 三、数据表的拼接 (一)merge () 函数的运用 (二)concat () 函数的运用 (三)append () 函数的运用 四、对数据表格的同级运算 五、计算数据表格中数…

组合问题(去重)

40. 组合总和 II - 力扣&#xff08;LeetCode&#xff09; class Solution { private:vector<vector<int>>result;vector<int>path;void backtracking(vector<int>& candidates, int target,int sum,int startIndex,vector<bool>&used)…

论QT6多线程技术

前言 以前我多线程使用传统的继承qthread重写run()或者继承qrunable类把对象丢到线程池解决。经过昨天的面试让我了解到新的技术&#xff0c;我之前看到过只不过没有详细的去了解movetotread技术&#xff0c;这个技术是qt5推出的&#xff0c;qt6还在延续使用 代码结构 以下是…

VTEP是什么

VTEP&#xff08;VXLAN Tunnel Endpoint&#xff0c;VXLAN 隧道端点&#xff09;是 VXLAN&#xff08;Virtual Extensible LAN&#xff09;网络中的关键组件&#xff0c;用于处理 VXLAN 流量的封装和解封装。以下以可读的 Markdown 格式详细解释 VTEP 的定义、功能、实现方式以…

antdv3 Tabs.TabPane 右上角增加一个角标Badge

1、Tabs官方说明 Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js 2、Badge角标官方效果图 Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js 3、Tabs.TabPane要实现的效果 4、代码 <Tabs v-m…

浅析 Spring 启动过程:从源码到核心方法

浅析 Spring 启动过程&#xff1a;从源码到核心方法 一、Spring 注解方式启动类 Demo二、Spring 启动过程源码解析​AnnotationConfigApplicationContext构造函数refresh()方法详解 三、refresh()的核心方法/步骤obtainFreshBeanFactory() - 获取Bean工厂prepareBeanFactory(be…

贝叶斯优化Transformer融合支持向量机多变量回归预测,附相关性气泡图、散点密度图,Matlab实现

贝叶斯优化Transformer融合支持向量机多变量回归预测&#xff0c;附相关性气泡图、散点密度图&#xff0c;Matlab实现 目录 贝叶斯优化Transformer融合支持向量机多变量回归预测&#xff0c;附相关性气泡图、散点密度图&#xff0c;Matlab实现效果一览基本介绍程序设计参考资料…

智慧化系统安全分析报告

智慧化系统的安全背景与现状 一、政策法规背景 &#xff08;一&#xff09;全球主要国家/地区政策对比 地区政策名称核心内容实施时间特点中国《生成式人工智能服务管理暂行办法》明确服务提供者责任&#xff0c;强调数据合法、隐私保护&#xff0c;禁止生成违法内容2023年8…

【学习笔记】点云自动化聚类简要总结

聚类是将将具有相似特征划分为相同点集的操作。 基于空间邻近性的方法 核心思想&#xff1a;依据点的空间距离进行分组 欧式聚类&#xff08;DBSCAN&#xff0c;KD-tree) 原理&#xff1a;基于半径搜索和最小点数扩展簇。 优点&#xff1a;适应不规则形状&#xff0c;无需预…

全志F10c200开发笔记——移植uboot

相关资料&#xff1a; &#xff08;二&#xff09;uboot移植--从零开始自制linux掌上电脑&#xff08;F1C200S)&#xff1c;嵌入式项目&#xff1e;-CSDN博客 F1C200S挖坑日记&#xff08;3&#xff09;——Uboot编译篇_f1c200s uboot-CSDN博客 一、安装编译器 Linaro Rele…

常见WEB漏洞----暴力破解

什么是暴力破解 暴力破解 (Brue Force) 是一种攻击方法 (穷举法)&#xff0c;简称为“爆破”&#xff0c;黑客通过反复猜解和实验&#xff0c;旨在以暴力手段登入、访问目标主机获取服务&#xff0c;破坏系统安全&#xff0c;其属于 ATT&CK技术中的一种&#xff0c;常利用…

ARM A64 LDR指令

ARM A64 LDR指令 1 LDR (immediate)1.1 Post-index1.2 Pre-index1.3 Unsigned offset 2 LDR (literal)3 LDR (register)4 其他LDR指令变体4.1 LDRB (immediate)4.1.1 Post-index4.1.2 Pre-index4.1.3 Unsigned offset 4.2 LDRB (register)4.3 LDRH (immediate)4.3.1 Post-index…

2.安卓逆向2-adb指令

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…