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;
    }
相关推荐
PieroPc13 分钟前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel
2401_857439693 小时前
SSM 架构下 Vue 电脑测评系统:为电脑性能评估赋能
开发语言·php
SoraLuna3 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
Dream_Snowar4 小时前
速通Python 第三节
开发语言·python
唐诺4 小时前
几种广泛使用的 C++ 编译器
c++·编译器
高山我梦口香糖5 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
信号处理学渣6 小时前
matlab画图,选择性显示legend标签
开发语言·matlab
红龙创客6 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++