当前位置:首页 » 《随便一记》 » 正文

C++初阶:适合新手的手撕list(模拟实现list)

8 人参与  2024年02月18日 16:21  分类 : 《随便一记》  评论

点击全文阅读


上次讲了常用的接口:今天就来进行模拟实现啦


文章目录

1.基本结构与文件规划2.空参构造函数(constructor)3.完善迭代器(iterator)(begin(),end())4.List Capacity(size(),empty())4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)6.clear()和swap()7. 完善构造函数7.1list (size_type n, const value_type& val = value_type());7.2利用迭代器进行构造7.3拷贝构造 8.重载=9.析构函数10.反向迭代器


1.基本结构与文件规划

请添加图片描述

list.h头文件:包含类的全部(函数的声明与定义)reverseIterator.h文件:进行反向迭代器的实现test.cpp源文件:进行调用test函数,测试和完善功能

基本结构:

#pragma oncenamespace MyList{    // List的节点类    template<class T>    struct ListNode    {        ListNode<T>* _prev;        ListNode<T>* _next;        T _data;        ListNode(const T& x=T())            :_prev(nullptr)            ,_next(nullptr)            ,_data(x)        {}    };    //List的迭代器类    template<class T, class Ref, class Ptr>    struct ListIterator    {        typedef ListNode<T> Node;        typedef ListIterator<T, Ref, Ptr> Self;                Node* _node;        ListIterator(Node* node)//构造函数            :_node(node)        {}        ListIterator(const Self& l)//拷贝构造函数            :_node(l._node)        {}        T& operator*();        T* operator->();        Self& operator++();        Self operator++(int);        Self& operator--();        Self& operator--(int);        bool operator!=(const Self& l);        bool operator==(const Self& l);    };    //list类    template<class T>    class list    {        typedef ListNode<T> Node;//默认是private 不给外面用    public:        typedef ListIterator<T, T&, T*> iterator;        typedef ListIterator<T, const T&, const T&> const_iterator;        //构造函数        list();        list(int n, const T& value = T());        template <class Iterator>        list(Iterator first, Iterator last);        //析构        ~list();        // List Iterator        iterator begin();        iterator end();        const_iterator begin();        const_iterator end();        // List Capacity        size_t size()const;        bool empty()const;           // List Access        T& front();        const T& front()const;        T& back();        const T& back()const;        // List Modify        void push_back(const T& val) { insert(end(), val); }        void pop_back() { erase(--end()); }        void push_front(const T& val) { insert(begin(), val); }        void pop_front() { erase(begin()); }        // 在pos位置前插入值为val的节点        iterator insert(iterator pos, const T& val);        // 删除pos位置的节点,返回该节点的下一个位置        iterator erase(iterator pos);        void clear();        void swap(list<T>& l);    private:        Node* _head;    };};
ListNode 结构体: 定义了链表的节点结构,包含了三个成员变量:前驱指针 _prev、后继指针 _next 和数据 _data。构造函数初始化了这些成员变量,允许在创建节点时指定初始值。 ListIterator 结构体: 定义了链表的迭代器结构,包含了指向节点的指针 _node。重载了一系列操作符,如 *->++--!===,以便于对链表进行遍历和操作。 list 类: 包含了迭代器的定义、构造函数、析构函数以及一系列的操作函数。定义了两种迭代器类型:iteratorconst_iterator,分别用于可修改的迭代和只读的迭代。实现了一系列的操作函数

2.空参构造函数(constructor)

        list()        {            _head = new Node;//去调用Node的默认构造函数了            _head->_next = _head;            _head->_prev = _head;//带头双向循环链表是这样的        }

使用new:动态开辟+调用默认构造函数了


3.完善迭代器(iterator)(begin(),end())

这里为什么我们要把迭代器封装为一个类呢?明明之前模拟vectorstring时,就直接typedef

之前模拟vectorstring时,二者底层都是连续的,想要到下一个可以直接++;想要得到里面的数据可以直接*

但是现在对于list是不行的,我们就需要重载各种运算符,但是底层又是一个指针(内置类型)不能重载,现在就只能封装进一个类里,就能重载了

 //List的迭代器类    template<class T, class Ref, class Ptr>    struct ListIterator    {        typedef ListNode<T> Node;        typedef ListIterator<T, Ref, Ptr> Self;                Node* _node;        ListIterator(Node* node)//构造函数            :_node(node)        {}        ListIterator(const Self& l)//拷贝构造函数            :_node(l._node)//这里是浅拷贝(写不写都行)            //新创建的迭代器和原迭代器指向相同的内存地址        {}        Ref operator*()        {            return _node->_data;        }        Ptr operator->()        {            return &(_node->_data);        }        Self& operator++()//前置        {            _node = _node->_next;//自己要更新            return *this;        }        Self operator++(int)        {            Self s(*this);            _node = _node->_next;            return s;        }        Self& operator--()        {            _node = _node->_prev;//自己要更新            return *this;        }        Self& operator--(int)        {            Self s(*this);            _node = _node->_prev;            return s;        }        bool operator!=(const Self& l)        {            return _node != l._node;        }        bool operator==(const Self& l)        {            return _node == l._node;        }    };    //list类    template<class T>    class list    {        typedef ListNode<T> Node;//默认是private 不给外面用    public:        typedef ListIterator<T, T&, T*> iterator;        typedef ListIterator<T, const T&, const T&> const_iterator;        //构造函数        list()        {            _head = new Node;//去调用Node的默认构造函数了            _head->_next = _head;            _head->_prev = _head;//带头双向循环链表是这样的        }        // List Iterator        iterator begin()        {            return _head->_next;//隐式类型转换(由单参构造函数支撑)        }        iterator end()        {            return _head;        }        const_iterator begin()        {            return _head->_next;        }        const_iterator end()        {            return _head;        }    private:        Node* _head;    };};

4.List Capacity(size(),empty())

       // List Capacity        size_t size()const        {            size_t size = 0;            iterator it = begin();            while (it != end())            {                size++;                ++it;            }            return size;        }        bool empty()const        {            return size() == 0;        }

4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)

        // List Modify        void push_back(const T& val) //尾插        {             insert(end(), val);         }        void pop_back() //尾删        {             erase(--end());        }        void push_front(const T& val) //头插        {             insert(begin(), val);        }        void pop_front()//头删        {             erase(begin());        }        // 在pos位置前插入值为val的节点        iterator insert(iterator pos, const T& val)        {            Node* cur = pos._node;            Node* prev = pos._node->_prev;            Node* newnode = new Node(val);//创建新节点            newnode->_next = cur;            cur->_prev = newnode;            newnode->_prev = prev;            prev->_next = cur;            return newnode;//隐式类型转换        }        // 删除pos位置的节点,返回该节点的下一个位置        iterator erase(iterator pos)        {            assert(pos != _head);            Node* cur = pos._node;            Node* prev = cur->_prev;            Node* next = cur->_next;            prev->_next = next;            next->_prev = prev;            delete cur;            return next;        }

使用test1函数看功能是否正常

void print(MyList::list<int>& lt){list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it; // 更新迭代器}cout << endl;}void test1(){MyList::list<int> lt;lt.push_back(1);lt.push_back(2);//尾插2个print(lt);lt.pop_back();//尾删一个print(lt);lt.push_front(0);//头插一个print(lt);lt.pop_front();//头删一个print(lt);}

请添加图片描述


6.clear()和swap()

       void clear()        {            //删除除头结点(_head)以外的节点            iterator it = begin();            while (it != end())            {                it = erase(it);            }        }        void swap(list<T>& l)        {            std::swap(_head, l._head);        }

7. 完善构造函数

7.1list (size_type n, const value_type& val = value_type());

list(int n, const T& value = T()){    _head = new Node;    _head->_next = _head;    _head->_prev = _head;    for (int i = 0; i < n; ++i)    {        push_back(value);    }}

请添加图片描述

7.2利用迭代器进行构造

    template <class Iterator>        list(Iterator first, Iterator last)        {            while (first != last)            {                push_back(*first);                first++;            }        }

为什么使用模版:

因为可能使用其他类型的迭代器来进行初始化

7.3拷贝构造

        list(const list<T>& lt)        {            _head = new Node;            _head->_next = _head;            _head->_prev = _head;            iterator it = copy.begin();            while (it != copy.end())            {                push_back(*it);                it++;            }        }

8.重载=

        list<T>& lt operator=(list<T> lt)        {            swap(lt);            return *this;        }

注意这里的参数不是常量引用,而是按值传递的。这是因为在赋值操作符中我们会调用 swap 函数,按值传递可以保证传入的参数会被复制一份,避免对原对象的修改。在函数体内,我们调用了 swap 函数,将当前对象和传入的对象进行内容交换,然后返回 *this,即当前对象的引用。


9.析构函数

        ~list()        {            clear();            delete _head;            _head = nullptr;        }

调用clear函数后,就只剩下头结点了


10.反向迭代器

我们再次使用封装的思想,封装一个反向迭代器进去

#pragma oncetemplate <class iterator,class Ref,class Pre>struct reserve_iterator{typedef reserve_iterator<iterator, Ref, Pre> self;iterator _it;reserve_iterator(iterator it):_it(it)  {}self& operator++(){--_it;return *this;}self operator++(int){self tmp = *this;--_it;return tmp;}self& operator--(){++_it;return *this;}self operator--(int){self tmp = *this;++_it;return tmp;}Ref operator*(){iterator tmp = _it;--tmp;return *tmp;}Pre operator->(){return &(operator*());}bool operator!=(const self& s){return _it != s._it;}bool operator==(const self& s){return _it == s._it;}};

此时那list类里就是这样:

请添加图片描述


好啦,list的内容也结束啦,下次就是Stack和Queue了。感谢大家支持!!!


点击全文阅读


本文链接:http://zhangshiyu.com/post/68971.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1