首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++】list的模拟实现

【C++】list的模拟实现

作者头像
lovevivi
发布2023-04-28 19:13:19
发布2023-04-28 19:13:19
5070
举报
文章被收录于专栏:萌新的日常萌新的日常

1.list 底层

list为任意位置插入删除的容器,底层为带头双向循环链表

begin() 代表第一个结点,end()代表最后一个结点的下一个

2. list的模拟实现

1. list_node 类设计

代码语言:javascript
复制
template<class T>
    struct list_node
    {
        list_node<T>* _next;
        list_node<T>* _prev;
        T _data;

    };

C++中,Listnode作为类名,而next和prev都是类指针,指针引用成员时使用->,而对象引用成员时使用 . 通过显示实例化,将两个类指针指定类型为T


2. list类如何调用类型

Listnode 代表类型 Listnode 代表类名 + 模板参数 才是类型 而_head 以及创建新节点前都需加上类型

3 .push_back(正常实现)

代码语言:javascript
复制
void push_back(const T&amp;x)//尾插
        {
            node* newnode = new node(x);
            node* tail = _head->_prev;
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;
        }

当我们想要创建一个节点时,需要调用node的构造函数 typedef list_node node ,node是由 list_node 类提供的


代码语言:javascript
复制
list_node(const T&amp; x=T())//list类的构造函数
            :_next(nullptr)
            , _prev(nullptr)
            , _data(x)
        {
        }

最好在构造函数处提供全缺省,对于内置类型int可以使用0,但对于自定义类型来说就不可以,所以为了满足泛型的要求,使用匿名对象调用默认构造函数

4. 迭代器的实现

若在数组上有一个int类型的指针,解引用是int类型的数据,再++可以加载下一个位置,因为物理空间是连续的


同样若在链表上,解引用类型为 node,再++不能到下一个节点,因为物理空间不连续 所以构造迭代器通过封装节点的指针来进行构造 (后面会讲)

第一个模板参数T

创建一个_list_iterator的类,来实现迭代器功能


代码语言:javascript
复制
template<class T>
    struct _list_iterator
    {
        typedef list_node<T> node;
        typedef _list_iterator<T> self;
        node* _node;
        
        _list_iterator(node* n)
            :_node(n)
        {

        }
        T&amp; operator*()//解引用 
        {
            return _node->_data;
        }
        _list_iterator<T>&amp; operator++()//返回迭代器
        {
            _node = _node->_next;//指向下一个节点
            return *this;
        }
        bool operator!=(const self&amp;s)
        {
            return _node != s._node;
        }
    };

在list类中,调用迭代器实现begin()和end()功能 typedef _list_iterator<T,T&,T*> iterator, 通过typedef 将_list_node类模板定义为iterator


代码语言:javascript
复制
iterator begin()
        {
            return iterator(_head->_next);//通过匿名对象访问第一个数据
        }
        iterator end()
        {
            return iterator(_head);//通过匿名对象访问最后一个数据的下一个
        }

在list类中实现begin()和end(),内部调用_list_node类的构造函数


const迭代器

假设第一个代表的是T * ,而第二个代表的 T * const,保护迭代器本身不可以被修改,而我们想要保护迭代器指向的内容不可被修改即 const T*


复制_list_iterator类中的内容,并将名字修改为const_list_iterator 只需修改*operator类型为cosnt T& ,说明解引用后的数据返回不能被修改


代码语言:javascript
复制
template<class T>
    struct _list_const_iterator
    {
        typedef list_node<T> node;
        typedef _list_const_iterator<T> self;
        node* _node;

        _list_const_iterator(node* n)
            :_node(n)
        {

        }
        const T&amp; operator*()//解引用 
        {
            return _node->_data;
        }
        self&amp; operator++()//前置++
        {
            _node = _node->_next;//指向下一个节点
            return *this;
        }
        self&amp; operator++(int)//后置++
        {
            self ret = *this;
            _node = _node->_next;
            return ret;
        }
        self&amp; operator--()//前置--
        {
            _node = _node->_prev;
            return *this;
        }
        self&amp; operator--(int)//后置--
        {
            self ret = *this;
            _node = _node->_prev;
            return ret;
        }
        bool operator!=(const self&amp; s)//!=
        {
            return _node != s._node;
        }
        bool operator==(const self&amp; s)//==
        {
            return _node == s._node;
        }
    };
第二个模板参数Ref

迭代器和const迭代器只有 *operator 的返回值不同, 只需在模板中添加一个参数即可使用一个迭代器实现迭代器和const 迭代器的功能


第三个模板参数Ptr

对于内置类型int使用解引用找到对应数据,而自定义类型需使用->寻找下一个节点


AA作为自定义类型,想要找到下一个节点需要使用->,在迭代器中 重载 - >

it->_a1,实际上是 it->->_a1, it->返回值为AA* ,再通过这个指针使用->指向_a1 但是为了增强可读性,省略了一个-> it->_a1 实际上为 it.operator->()->._a1



对list封装的理解

在不考虑封装的情况下,两者等价


从物理空间上来看,it与pnode都是指向1的地址



pnode作为一个原生指针,解引用指针就会拿到这个地址,找到这个地址指向空间的内容 ++pnode,找到下一个节点的地址,但是下一个节点不一定是要的节点 *it 识别成为自定义类型就会调用函数

5. insert

代码语言:javascript
复制
void insert(iterator pos,const T&amp;x)//在pos位置前插入x
        {
            node* cur = pos._node;
            node* prev = cur->_prev;
            node* newnode = new node(x);
            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;
        }

6.push_back与 push_front(复用)

两者都可以通过复用 insert的方式实现

代码语言:javascript
复制
void push_back(const T&amp;x)//尾插
        {
            /*node* tail = _head->_prev;
            node* newnode = new node(x);
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;*/
            insert(end(), x);
        }
        void push_front(const T&amp;x)//头插
        {
            insert(begin(), x);
        }

7. erase

代码语言:javascript
复制
void erase(iterator pos)//删除pos位置
        {
            //头节点不可以删除
            assert(pos != end());
            node* cur = pos._node;
            node* prev = cur->_prev;
            node* next = cur->_next;
            prev->_next = next;
            next->_prev = prev;
            delete cur;
        }

由于头节点不可以删除,所以使用assert断言的方式

8. pop_back与pop_front (复用)

代码语言:javascript
复制
                void pop_back()//尾删
        {
            erase(--end());
        }
        void pop_front()//头删
        {
            erase(begin());
        }

9. clear 清空数据

代码语言:javascript
复制
void clear()//清空数据
            //但是要注意不要把head清掉
        {

            iterator it = begin();
            while (it != end())
            {
                it=erase(it);//为了防止迭代器失效设置返回值
                 //返回值为当前节点的下一个
            }
        }

迭代器失效是指迭代器所指向的节点失效 即节点被删除了 erase函数执行后,it所指向的节点被删除,因此it无效,在下一次使用it时,必须先给it赋值



为了防止迭代器失效所以使erase设置返回值,返回值为当前节点的下一个

10. 迭代器区间构造

代码语言:javascript
复制
void empty_init()
        {
            _head = new node;
            _head->_next = _head;
            _head->_prev = _head;
        }
template <class Iterator>
        list(Iterator first, Iterator last)
        {
            empty_init();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

想要尾插的前提时,需要有头节点的存在,所以设置一个函数对头节点初始化


12. 拷贝构造

传统写法
代码语言:javascript
复制
void empty_init()
        {
            _head = new node;
            _head->_next = _head;
            _head->_prev = _head;
        }
list(const list<T>&amp; It)//拷贝构造  传统写法
        {
            empty_init();//初始化头节点
            for (auto e : It)
            {
                push_back(e);
            }
        }
现代写法
代码语言:javascript
复制
void swap(list<T>&amp; tmp)
        {
            std::swap(_head, tmp._head);
        }
        list(const list<T>&amp; It)//拷贝构造现代写法
        {
            empty_init();//将头节点初始化
            list<T> tmp(It.begin(), It.end());
            swap(tmp);
        }

通过调用std中的swap来达到交换的目的

13. 赋值

代码语言:javascript
复制
void swap(list<T>&amp; tmp)
        {
            std::swap(_head, tmp._head);
        }
list<T>&amp; operator=(list<T> It)
        {
            swap(It);
            return *this;
        }

参数不可以使用 list &,虽然可以达到赋值的目的,但是It的值也会发生改变



3.完整代码

代码语言:javascript
复制
#include<iostream>
#include<list>
#include<assert.h>
using namespace std;
namespace yzq
{
    template<class T>
    struct list_node
    {
        list_node<T>* _next;
        list_node<T>* _prev;
        T _data;

        list_node(const T&amp; x=T())//构造函数
            :_next(nullptr)
            , _prev(nullptr)
            , _data(x)
        {
        }
    };
    //迭代器
    template<class T,class Ref,class Ptr>
    struct _list_iterator
    {
        typedef list_node<T> node;
        typedef _list_iterator<T,Ref,Ptr> self;
        node* _node;
        _list_iterator(node* n)
            :_node(n)
        {

        }
        Ref operator*()//解引用 
        {
            return _node->_data;
        }
        Ptr operator->()//->
        {
            return &amp;_node->_data;
        }
        self&amp; operator++()//前置++
        {
            _node = _node->_next;//指向下一个节点
            return *this;
        }
        self&amp; operator++(int)//后置++
        {
            self ret = *this;
            _node = _node->_next;
            return ret;
        }
        self&amp; operator--()//前置--
        {
            _node = _node->_prev;
            return *this;
        }
        self&amp; operator--(int)//后置--
        {
            self ret = *this;
            _node = _node->_prev;
            return ret;
        }
        bool operator!=(const self&amp;s)//!=
        {
            return _node != s._node;
        }
        bool operator==(const self&amp; s)//==
        {
            return _node == s._node;
        }
    };
    //const迭代器
    //template<class T>
    //struct _list_const_iterator
    //{
    //	typedef list_node<T> node;
    //	typedef _list_const_iterator<T> self;
    //	node* _node;

    //	_list_const_iterator(node* n)
    //		:_node(n)
    //	{

    //	}
    //	const T&amp; operator*()//解引用 
    //	{
    //		return _node->_data;
    //	}
    //	self&amp; operator++()//前置++
    //	{
    //		_node = _node->_next;//指向下一个节点
    //		return *this;
    //	}
    //	self&amp; operator++(int)//后置++
    //	{
    //		self ret = *this;
    //		_node = _node->_next;
    //		return ret;
    //	}
    //	self&amp; operator--()//前置--
    //	{
    //		_node = _node->_prev;
    //		return *this;
    //	}
    //	self&amp; operator--(int)//后置--
    //	{
    //		self ret = *this;
    //		_node = _node->_prev;
    //		return ret;
    //	}
    //	bool operator!=(const self&amp; s)//!=
    //	{
    //		return _node != s._node;
    //	}
    //	bool operator==(const self&amp; s)//==
    //	{
    //		return _node == s._node;
    //	}
    //};

    template <class T>
    class list
    {
        typedef list_node<T> node;
    public:
        typedef _list_iterator<T,T&amp;,T*> iterator;
        typedef _list_iterator<T, const T&amp;, const T*> const_iterator;//const迭代器
        //typedef _list_const_iterator<T> const_iterator;
        void empty_init()
        {
            _head = new node;
            _head->_next = _head;
            _head->_prev = _head;
        }
        list()//构造函数
        {
            empty_init();
        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            empty_init();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        //list(const list<T>&amp; It)//拷贝构造
        //{
        //	empty_init();//初始化头节点
        //	for (auto e : It)
        //	{
        //		push_back(e);
        //	}
        //}
        void swap(list<T>&amp; tmp)
        {
            std::swap(_head, tmp._head);
        }
        list(const list<T>&amp; It)//拷贝构造现代写法
        {
            empty_init();//将头节点初始化
            list<T> tmp(It.begin(), It.end());
            swap(tmp);
        }
        list<T>&amp; operator=(list<T> It)//赋值
        {
            swap(It);
            return *this;
        }
        ~list()//析构函数
        {
            //将头节点也要释放掉
            clear();
            delete _head;
            _head = nullptr;
        }
        void clear()//清空数据
            //但是要注意不要把head清掉
        {

            iterator it = begin();
            while (it != end())
            {
                it=erase(it);//为了防止迭代器失效设置返回值
                 //返回值为当前节点的下一个
            }
        }

        
        void push_back(const T&amp;x)//尾插
        {
            /*node* tail = _head->_prev;
            node* newnode = new node(x);
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;*/
            insert(end(), x);
        }
        void push_front(const T&amp;x)//头插
        {
            insert(begin(), x);
        }
        void insert(iterator pos,const T&amp;x)//在pos位置前插入x
        {
            node* cur = pos._node;
            node* prev = cur->_prev;
            node* newnode = new node(x);
            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;
        }
        iterator erase(iterator pos)//删除pos位置
        {
            //头节点不可以删除
            assert(pos != end());
            node* cur = pos._node;
            node* prev = cur->_prev;
            node* next = cur->_next;
            prev->_next = next;
            next->_prev = prev;
            delete cur;
            return iterator(next);
        }
        void pop_back()//尾删
        {
            erase(--end());
        }
        void pop_front()//头删
        {
            erase(begin());
        }
        iterator begin()
        {
            return iterator(_head->_next);//通过匿名对象访问第一个数据
        }
        iterator end()
        {
            return iterator(_head);//通过匿名对象访问最后一个数据的下一个
        }
        const_iterator begin()const
        {
            return const_iterator(_head->_next);
        }
        const_iterator end()const
        {
            return const_iterator(_head);
        }
    private:
        node* _head;
    };
    
    /*void test(const list<int>&amp;e)
    {
        list<int>::const_iterator it = e.begin();
            while (it != e.end())
            {
                    cout << *it << " ";
                ++it;
            }
            cout << endl;
    }
    void test2()
    {
        const list<int> v;
        test(v);

    }*/
    //void test1()
    //{
    //	list<int> v;
    //	v.push_back(1);
    //	v.push_back(2);
    //	v.push_back(3);
    //	v.push_back(4);
    //	list<int>::iterator it= v.begin();
    //	while (it != v.end())
    //	{
    //		cout << *it << " ";
    //		++it;
    //	}
    //	cout << endl;
    //}
    /*struct AA
    {
        int	_a1;
        int _a2;
        AA(int a1=0,int a2=0)
            :_a1(a1)
            ,_a2(a2)
        {
        }
    };*/
    /*void test3()
    {
        list<AA>v;
        v.push_back(AA(1, 1));
        v.push_back(AA(2, 2));
        v.push_back(AA(3, 3));
        list<AA>::iterator it = v.begin();
        while (it != v.end())
        {
            cout << it->_a1 << " :"<<it->_a2<<" ";
            cout << endl;
            it++;
        }
    }*/
    //void test4()
    //{
    //	list<int> v;
    //	v.push_back(1);
    //	v.push_back(2);
    //	v.push_back(3);
    //	v.push_back(4);// 1 2 3 4
    //	for (auto e : v)
    //	{
    //		cout << e << " ";
    //	}
    //	cout << endl;
    //	v.clear();
    //	v.push_back(4);//  4
    //	for (auto e : v)
    //	{
    //		cout << e << " ";
    //	}
    //}
    //void test4()
    //{
    //	list<int> v;
    //	v.push_back(1);
    //	v.push_back(2);
    //	v.push_back(3);
    //	v.push_back(4);// 1 2 3 4
    //	for (auto e : v)
    //	{
    //		cout << e << " ";
    //	}
    //	cout << endl;
    //	list<int> v2(v);
    //	for (auto e : v2)// 1 2 3 4
    //	{
    //		cout << e << " ";
    //	}
    //}
    void test4()
    {
        list<int> v;
        v.push_back(1);
        v.push_back(2);//v1 = 1 2
        list<int> v2;
        v2.push_back(5);
        v2.push_back(6);//v2=5 6
            v2 = v;
        for (auto e : v2)// v2=1 2 
        {
            cout << e << " ";
        }
        cout << endl;
        for (auto e : v)// v1=1 2
        {
            cout << e << " ";
        }
    }
}	

int main()
{
    yzq::test4();
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.list 底层
  • 2. list的模拟实现
    • 1. list_node 类设计
    • 2. list类如何调用类型
    • 3 .push_back(正常实现)
    • 4. 迭代器的实现
      • 第一个模板参数T
      • const迭代器
      • 第二个模板参数Ref
      • 第三个模板参数Ptr
      • 对list封装的理解
    • 5. insert
    • 6.push_back与 push_front(复用)
    • 7. erase
    • 8. pop_back与pop_front (复用)
    • 9. clear 清空数据
    • 10. 迭代器区间构造
    • 12. 拷贝构造
      • 传统写法
      • 现代写法
    • 13. 赋值
  • 3.完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档