C++——list常见函数的使用和模拟实现(1)

容器list的本质就是我们之前模拟实现过的链表中的双向带头循环链表,因此在使用的时候和之前我们实现的链表的功能是类似的。但是作为容器,list相较于我们之前实现的链表增加了迭代器的使用,同时这里的迭代器和之前模拟实现string和vector的迭代器也有所不同。因为string和vector的迭代器都可以用原生的指针来模拟实现,但是list中的每一个结点都是分散的,同时每个结点还要保存上一个结点以及下一个结点的信息,所以用原生的指针来实现不是理想,具体的实现方式会在后面具体提到。

一、构造函数

list作为容器也有多种构造函数,这里我们只对构造一个空的list进行模拟实现,其中只进行使用介绍。

链表中的结点不仅需要存储数据,同时还要记录前后结点的信息,因此我们需要用一个结构体来作为链表的结点。因为我们的list能够存储各种类型的数据,所以这里存储的数据类型要用模版来代替。

cpp 复制代码
	template<class T>
	struct list_Node
	{
		T _data;
		list_Node* _next;
		list_Node* _prev;
        //T的类型是自定义类型时,T()会调用T类型的构造函数
        //当T是内置类型是,T()会将T初始化为默认值
		list_Node(const T& data = T())
			:_data(data)
			,_next(nullptr)
			,_prev(nullptr)
		{
		}
	};

由于list的本质上是一个双向带头的循环链表是,所以我们只需要记录list的头结点和就能找到这一整个链表了,同时我们还需要记录一下list内部数据的个数方便后续的操作。因为list能够存储的数据类型是不确定的,我们的结点是一个模版类,所以我们的list也要是一个模版类。它的基本结构如下:

cpp 复制代码
template<class T>
class list
{
    //在后面的操作中我们会经常用到结点的类型,因此我们直接把它typedef成Node方便操作
    typedef list_Node<T> Node;

    Node* _head;
    size_t _size;
}

因为list的本质是一个双向带头的循环链表,所以当构造出一个链表时,它应该要一个用来标记的头结点,不论链表中的是否有数据,或者有多少个数据,我们链表都要满足它的基本结构的特点,所以这个用来标记的头结点也把它指向前后结点的指针都指向自己,这样才满足了条件。

cpp 复制代码
list()
{
    _head = new Node;
    _head->_next = _head;
    _head->_prev = _head;
    _size = 0;			
}
cpp 复制代码
//构造一个包含n个值为val的list
list (size_type n, const value_type& val =value_type())
cpp 复制代码
//用任意迭代器把[first,last)的数据存入一个list中
list (InputIterator first, InputIterator last)
cpp 复制代码
//list的拷贝构造函数,可以用一个list来构造另外一个list
list (const list& x)

二、list常见的插入和删除

和我们实现的list类似,容器list也分别有三种插入和删除的方式,push_back在链表的尾部插入一个数据,push_front在链表的头部插入一个数据,insert在指定位置前插入一个数据;pop_back删除链表尾部的数据,pop_front删除链表头部的数据,erase删除链表指定位置的数据。

这里先模拟实现在 头部和尾部操作数据的函数,在特定位置操作的函数放在实现了迭代器以后再进行模拟实现。

cpp 复制代码
    void push_back(const T& x)
    {
        //循环链表的首尾相连 头结点的前一个结点就是链表的最后一个数据
        //先保存下插入前尾部数据的信息 方便后续操作
        Node* cur = _head->_prev; 
        Node* newnode = new Node(x);
        //把数据先接到头结点之前的位置 也就是最后一个数据的位置
        _head->_prev = newnode;
        newnode->_next = _head;
        //再把插入前的最后一个数据和新的尾部数据连接起来
        cur->_next= newnode;
        newnode->_prev = cur; 
        ++_size;
    }
cpp 复制代码
    void push_front(const T& x)
    {
        //保存插入前的头部数据
        Node* cur = _head->next;
        Node* newnode = new Node(x);
        //先把新的数据挂在头结点的后面
        _head->_next = newnode;
        newnode->_prev = _head;
        //再把原先的头部数据接到新的头部数据的后面
        cur->_prev = newnode;
        newnode->_next = cur;
        ++_size;
    }
cpp 复制代码
    pop_back()
    {
        //保存尾部数据的信息
        Node* cur = _head->_prev;
        //将尾部数据前一个结点中指向下一个结点的指针指向头结点
        //再把头结点指向前一个结点的指针指向尾部数据的前一个结点
        //改变这两个指针的指向就可以删除尾部的数据
        _head->prev = cur->prev;
        cue->prev->next = _head;
        //再释放掉被删除结点申请的空间
        delete cur;
        --_size;
    }
cpp 复制代码
    pop_front()
    {
        //保存原先的头部数据的信息
        Node* cur =_head->_next;
        //把头结点指向下一个结点的指针指向头部数据的下一个节点
        //再把头部数据下一个结点指向前一个结点的指针指向头结点
        //这样原先的头部数据就被踢出链表了
        _head->_next =cur->_next;
        cur->next->prev =_head;
        //释放被删除结点申请的空间
        delete cur;
        --_size;
    }
相关推荐
monkey_meng11 分钟前
【Rust中的项目管理】
开发语言·rust·源代码管理
喜欢打篮球的普通人13 分钟前
rust高级特征
开发语言·后端·rust
ModelBulider31 分钟前
十三、注解配置SpringMVC
java·开发语言·数据库·sql·mysql
V搜xhliang024640 分钟前
基于深度学习的地物类型的提取
开发语言·人工智能·python·深度学习·神经网络·学习·conda
DK七七42 分钟前
多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码
开发语言·前端·微信小程序·小程序·php
苹果酱05671 小时前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
代码小鑫1 小时前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
训山1 小时前
4000字浅谈Java网络编程
java·开发语言·网络
API快乐传递者1 小时前
除了网页标题,还能用爬虫抓取哪些信息?
开发语言·爬虫·python
hutaotaotao2 小时前
c语言用户不同命令调用不同函数实现
c语言·开发语言