C++list的模拟实现

一、list模拟实现的接口

list的重要接口就是上面这些接口,我们主要实现的就是这几个接口。

要模拟实现list,我们需要定义三个类,一个是list的节点类,一个是list的迭代器类,还有就是list类,与我们c语言实现链表相比,只是多了个迭代器类。

二、list节点类的模拟实现

// List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T());
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

这就是list的节点类,该类用于存放节点的数据,像节点的值、前驱和后继指针。

在这我们要实现一个构造函数,将链表的节点进行初始化:

ListNode(const T& val = T())
    :_val(val)
    ,_pPre(nullptr)
    ,_pNext(nullptr)
{}

将val赋给_val,将前驱和后继指针置为空即可。

三、list迭代器类的模拟实现

//List的迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(PNode pNode = nullptr);
        ListIterator(const Self& l);
        T& operator*();
        T* operator->();
        Self& operator++();
        Self operator++(int);
        Self& operator--();
        Self& operator--(int);
        bool operator!=(const Self& l);
        bool operator==(const Self& l);
        PNode _pNode;
    };

 这就是list的迭代器类,在实现迭代器时,我们用到了三个模版参数,T表示迭代器指向节点的数据类型,Ref是T的引用,以便在需要修改节点数据时能够进行修改,Ptr是T*,是指针类型,用于返回节点数据的指针,而成员变量_pNode是一个指向节点的指针,这个指针用于在链表中定位当前迭代器所指向的节点,从而实现对链表的遍历和操作。

下面我们来一一实现list迭代器类的成员函数。

3.1 list迭代器类构造函数

ListIterator(PNode pNode = nullptr)
    :_pNode(pNode)
{}
ListIterator(const Self& l)
{
    _pNode = l._pNode;
}

 构造函数十分简单,只需将pNode的值赋给_pNode即可。

3.2 list迭代器类指针操作运算符

T& operator*()
{
    return _pNode->_val;
}
T* operator->()
{
    return &(_pNode->_val);
}

这里实现的是指针的操作运算符*和->,*用于获取当前节点的值,->用于获取当前节点数据的地址,使得可以通过迭代器像使用指针一样访问节点数据的成员。

3.3  list迭代器类自增自减

 Self& operator++()//前置++
 {
     _pNode = _pNode->_pNext;
     return *this;
 }
 Self operator++(int)//后置++
 {
     Self temp(*this);
     _pNode = _pNode->_pNext;
     return temp;
 }
 Self& operator--()//前置--
 {
     _pNode = _pNode->_pPre;
     return *this;
 }
 Self& operator--(int)//后置--
 {
     Self temp(*this);
     _pNode = _pNode->_pPrev;
     return temp;
 }

list迭代器的自增自减就是查找当前节点的前后节点,前置的先找到前驱或后置节点再返回,后置的先返回再找到前驱或后置节点。

 3.4 list迭代器类不等与相等判断运算符

 bool operator!=(const Self& l)
 {
     return _pNode != l._pNode;
 }
 bool operator==(const Self& l)
 {
     return _pNode == l._pNode;
 }

判断相等和不相等十分简单,直接比较他们的指针值即可实现。 

 

四、list类的模拟实现

 //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T*> const_iterator;
    public:
        ///
        // List的构造
        list();
        list(int n, const T& value = T());
        template <class Iterator>
        list(Iterator first, Iterator last);
        list(const list<T>& l);
        list<T>& operator=(const list<T> l);
        ~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:
        PNode _pHead;
        size_t _size;

这就是list类,对于一个链表,我们需要一个哨兵位头结点,也需要一个_size来记录链表的长度。

通过哨兵位头结点我们可以很快找到尾结点,也可以通过哨兵位来遍历整个链表。

下面我们来一一实现list类的成员函数。

4.1 list类构造函数

void empty_init()
{
    _pHead = new Node;
    _pHead->_pNext = _pHead;
    _pHead->_pPre = _pHead;
    _size = 0;
}

list()
{
    empty_init();
}
list(int n, const T& value = T())
{
    empty_init();
    for (size_t i = 0; i < n; i++)
    {
        push_back(value);
    }
}
template <class Iterator>
list(Iterator first, Iterator last)
{
    empty_init();
    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}
list(const list<T>& l)
{
    empty_init();
    for (auto& x : l)
    {
        push_back(x);
    }
}

为了方便,我们可以写一个空构造函数,创建个哨兵位节点,令其前驱和后继指针指向其自己,再将_size置为0,这样便创建出了一个空链表,方便编写代码。用n个value初始化、迭代器初始化和拷贝构造的原理都差不多,都是遍历然后尾插到创建的空链表上。

4.2 list类析构函数和赋值重载

list<T>& operator=(const list<T> l)
{
    empty_init();
    swap(l);
    return *this;
}
~list()
{
    clear();
    delete _pHead;
    _pHead = nullptr;
}

赋值重载只需将两个链表进行交换即可,析构函数可以复用clear()函数,clear函数将除头结点外的所有节点全部删除,最后我们只需手动删除头结点并将其置为空。

4.3 list类begin和end

iterator begin()
{
    return _pHead->_pNext;
}
iterator end()
{
    return _pHead;
}
const_iterator begin() const
{
    return _pHead->_pNext;
}
const_iterator end() const
{
    return _pHead;
}

begin和end分别返回链表的头和尾,因为是实现的链表为循环双向链表,所以链表的头为哨兵位的下一个节点,链表的尾即为头结点。 

4.4 list类front和back

T& front()
{
    return _pHead->_pNext->_val;
}
const T& front() const
{
    return _pHead->_pNext->_val;
}
T& back()
{
    return _pHead->_pPre->_val;
}
const T& back() const
{
    return _pHead->_pPre->_val;
}

front和back分别返回链表有效值第一位和最后一位的值,所以我们返回头结点下一个的值和前一个的值即可。 

4.5 list类push_back和pop_back

void push_back(const T& val) 
{
  insert(end(), val); 
}
void pop_back() 
{ 
  erase(--end()); 
}

insert是在pos位置之前进行插入,在头结点位置之前进行插入就是进行尾插,尾删复用erase,--end()后的位置就是有效数据的尾,就可实现尾删。 

4.6 list类push_front和pop_front

void push_front(const T& val) 
{ 
  insert(begin(), val); 
}
void pop_front() 
{ 
   erase(begin()); 
}

begin返回的便是链表的头,直接复用insert和erase即可。 

4.7 list类insert

// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    PNode newnode = new Node(val);
    newnode->_pNext = pos._pNode;
    newnode->_pPre = pos._pNode->_pPre;
    pos._pNode->_pPre->_pNext = newnode;
    pos._pNode->_pPre = newnode;
    ++_size;
    return newnode;
}

在pos位置之前进行插入,先创建一个新节点,然后修改指针指向即可,最后++_size,即可完成插入。

4.8 list类erase

// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
    assert(pos != end());
    PNode cur = pos._pNode;
    PNode prev = cur->_pPre;
    PNode next = cur->_pNext;
    prev->_pNext = next;
    next->_pPre = prev;
    delete pos._pNode;
    --_size;
    return next;
}

删除链表节点,一样修改指针指向即可,因为要返回删除节点而得下一个位置,所以这里的返回值是next。

这里要注意的一个问题是迭代器失效的问题,我们来看下面的代码:

void TestListIterator1()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    auto it = l.begin();
    while (it != l.end())
    {
        l.erase(it);
        ++it;
    }
}

我们在执行上述代码时,程序会崩溃,这时因为erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值。

修改后:

void TestListIterator()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    auto it = l.begin();
    while (it != l.end())
    {
       it = l.erase(it);
    }
}

每次erase完后如果还想使用it,就必须要对it进行修改,才能再次使用。 

4.9 lsit类clear和swap

void clear()
{
    iterator it = begin();
    while (it != end())
    {
        it = erase(it);
    }
    _size = 0;
}
void swap(list<T>& l)
{
    std::swap(_pHead, l->_pHead);
    std::swap(_size, l->_size);
}

claer函数只需遍历链表,将每个节点复用erase进行删除即可,这里需要注意的是,因为erase返回的是删除位置下一个位置的指针,所以这里不需要再++it,而swap只需调用标准库的swap,将两个链表的头结点和_size进行交换即可。

五、list模拟实现完整代码

list.h文件:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;


namespace bite
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())
            :_val(val)
            ,_pPre(nullptr)
            ,_pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };


    //List的迭代器类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        ListIterator(const Self& l)
        {
            _pNode = l._pNode;
        }
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &(_pNode->_val);
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPrev;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;
        }
        PNode _pNode;
    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T*> const_iterator;
    public:
        ///
        // List的构造
        void empty_init()
        {
            _pHead = new Node;
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
            _size = 0;
        }

        list()
        {
            empty_init();
        }
        list(int n, const T& value = T())
        {
            empty_init();
            for (size_t i = 0; i < n; i++)
            {
                push_back(value);
            }
        }
        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            empty_init();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        list(const list<T>& l)
        {
            empty_init();
            for (auto& x : l)
            {
                push_back(x);
            }
        }
        list<T>& operator=(const list<T> l)
        {
            empty_init();
            swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }


        ///
        // List Iterator
        iterator begin()
        {
            return _pHead->_pNext;
        }
        iterator end()
        {
            return _pHead;
        }
        const_iterator begin() const
        {
            return _pHead->_pNext;
        }
        const_iterator end() const
        {
            return _pHead;
        }


        ///
        // List Capacity
        size_t size() const
        {
            return _size;
        }
        bool empty() const
        {
            return _size == 0;
        }


        
        // List Access
        T& front()
        {
            return _pHead->_pNext->_val;
        }
        const T& front() const
        {
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            return _pHead->_pPre->_val;
        }
        const T& back() const
        {
            return _pHead->_pPre->_val;
        }


        
        // 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)
        {
            PNode newnode = new Node(val);
            newnode->_pNext = pos._pNode;
            newnode->_pPre = pos._pNode->_pPre;
            pos._pNode->_pPre->_pNext = newnode;
            pos._pNode->_pPre = newnode;
            ++_size;
            return newnode;
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(pos != end());
            PNode cur = pos._pNode;
            PNode prev = cur->_pPre;
            PNode next = cur->_pNext;
            prev->_pNext = next;
            next->_pPre = prev;
            delete pos._pNode;
            --_size;
            return next;
        }
        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
            _size = 0;
        }
        void swap(list<T>& l)
        {
            std::swap(_pHead, l->_pHead);
            std::swap(_size, l->_size);
        }
    private:
        PNode _pHead;
        size_t _size;
    };
    template<class container>
    void print_container(const container& lt)
    {
        for (auto x : lt)
        {
            cout << x << " ";
        }
        cout << endl;
    }

    void test_operator()
    {
        list<int> l1;
        l1.push_back(1);
        l1.push_back(2);
        l1.push_back(3);
        l1.push_back(4);
        print_container(l1);
        list<int> l2(l1);
        print_container(l2);
    }

    void test_back_front()
    {
        list<int> l1;
        l1.push_back(1);
        l1.push_back(2);
        l1.push_back(3);
        l1.push_back(4);
        print_container(l1);
        cout << l1.front() << endl;
        cout << l1.back() << endl;
    }

    void test_push_back()
    {
        list<int> l1(3,1);
        print_container(l1);
        l1.push_back(2);
        print_container(l1);
    }

    void test_push_front()
    {
        list<int> l1(3, 1);
        print_container(l1);
        l1.push_front(2);
        print_container(l1);
    }

    void test_insert()
    {
        list<int> l1;
        l1.push_back(1);
        l1.push_back(2);
        l1.push_back(3);
        l1.push_back(4);
        print_container(l1);
        list<int>::iterator it = l1.begin();
        l1.insert(it, 0);
        print_container(l1);
    }

    void test_erase()
    {
        list<int> l1;
        l1.push_back(1);
        l1.push_back(2);
        l1.push_back(3);
        l1.push_back(4);
        print_container(l1);
        list<int>::iterator it = l1.begin();
        l1.erase(it);
        print_container(l1);
    }

    void TestListIterator1()
    {
        int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        list<int> l(array, array + sizeof(array) / sizeof(array[0]));
        auto it = l.begin();
        while (it != l.end())
        {
            // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
            l.erase(it);
            ++it;
        }
    }

    void TestListIterator()
    {
        int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        list<int> l(array, array + sizeof(array) / sizeof(array[0]));
        auto it = l.begin();
        while (it != l.end())
        {
           it = l.erase(it);
        }
    }
};

 

test.cpp文件: 

#include"List.h"

int main()
{
	//bite::test_operator();
	//bite::test_back_front();
	//bite::test_push_back();
	//bite::test_push_front();
	//bite::test_insert();
	//bite::test_erase();
	//bite::TestListIterator1();
	bite::TestListIterator();
	return 0;
}

 

六、总结

以上就是模拟实现list的全部内容,希望以上所讲能够对你有所帮助,如果对你有帮助的话,记得一键三连哦,感谢各位。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和提供了关于实现vector的两种方法。其中,引用展示了一个使用reserve和push_back方法的示例,而引用展示了一个使用new和memcpy函数的示例。这两种方法都是常见的实现vector的方式。 在第一种方法中,通过reserve函数可以预留足够的内存空间,然后使用push_back函数逐个将元素添加到vector中。这种方法的好处是可以避免不必要的内存重分配,提高了效率。 而第二种方法使用new操作符在堆上分配内存空间,并使用memcpy函数将已有的vector对象的数据复制到新的内存空间中。通过这种方式,可以实现深拷贝,即两个vector对象拥有独立的内存空间。这种方法的好处是可以在不修改原始vector对象的情况下创建一个新的vector对象。 除了以上两种方法,还可以使用其他方式实现vector类。例如,可以使用动态数组来实现vector的底层数据结构,然后通过成员函数实现vector的各种操作,如增加、删除、查找等。 总结来说,c语言模拟实现vector的关键是动态内存管理和对元素的增删改查操作。可以使用预留空间和逐个添加元素的方式,也可以使用动态数组和复制数据的方式来实现vector类。具体的实现方式可以根据需求和实际情况选择。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++——vector模拟实现](https://blog.csdn.net/weixin_49449676/article/details/126813526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值