list的模拟实现

第一步:看源代码
类的框架:
成员函数:
 
基本可以确定list是一个带头双向循环链表,end()的迭代器指向头节点,begin()的迭代器指向头结点的下一个节
list的迭代器:(稍显复杂)
库中的迭代器成员函数只有一个链表节点指针。对链表节点指针直接解引用只能拿到节点空间,没办法拿到节点中存储的date,但是可以通过自定义类型封装间接实现拿到date。节点的指针++,也不会到达下一个地址。自定义类型的运算符重载可以控制行为。
下面是具体的实现:
list.h中
#pragma once
#include<iostream>
#include<algorithm>
#include<assert.h>
using namespace std;
namespace bit
{
        template<class T>
        struct _list_node
        {
               _list_node(const T& x = T()) //这里的缺省不能直接给0,因为T的类型可能为string,vector
               {
                       _date = x;
                       _next = nullptr;
                       _prev = nullptr;
               }
               _list_node<T>* _next;
               _list_node<T>* _prev;
               T _date;
        };
         //list的迭代器是一个难点
        //template<class T>最开始的框架
        //typedef _list_iterator<T, T&, T*> iterator;
        //typedef _list_iterator<T, const T&, const T*> const_iterator;
        //当调用begin()的类型不同时匹配的begin()函数不同,决定了是返回const_iterator类型还是iterator类型,
        //由此决定ref具体是什么类型,ptr具体是什么类型。
        template<class T, class ref, class ptr>
         //源代码中模板参数有三个template<class T, class Ref, class Ptr>这个和const迭代器有关。
        struct _list_iterator
        {
               typedef _list_node<T> Node;
               typedef  _list_iterator<T,ref,ptr> Self;
               Node* _node; //迭代器器的本质是一个节点指针,和_head一样。
               _list_iterator(Node* it) //如果不想被隐式类型转换可以这样声明:explicit _list_iterator(Node* it)
                       :_node(it)
               {}
                //需要写析构函数吗?
               //不需要,_node指向的是节点空间,不能说使用一次迭代器就释放节点空间,所以使用默认生成的析构函数就可以
               //默认生成的析构函数不会对指针类型进行处理
               //需要写拷贝构造和赋值重载吗?
               //不需要,指针为内置类型,默认生成的浅拷贝就可以。
               ref operator*()
                //开始的框架:T& operator*()
               {
                       return _node->_date; //_date是节点中的数据,节点是new出来的,出作用域不会消失,可以引用返回。
               }
                 //开始的框架:T* operator->()
               ptr operator->() // ptr operator-> ()是干什么的?
                                        //当T的实参为类时会用到。在下面test.cpp中test2有示范
               {
                       return &(_node->_date);
               }
               bool operator==(const Self& pos)
               {
                       return _node == pos._node;
               }
               bool operator!=(const Self& pos) //pos是_list_iterator<T>类型
               {
                       return _node != pos._node; //这里必须用_node进行比较,如果用迭代器比较,会再进行!=重载,形成死循环。
                       //this是指向迭代器的指针,是_list_iterator<T>*类型
               }
               Self& operator++()
               {
                       _node = _node->_next;
                       return *this; //_node是_list_node<T>*类型,不能转换为_list_iterator<T>&类型
                       //如果没有&,就可以,因为支持单参数隐式类型转换。
               }
               Self operator++(int)
               {
                       Self tmp = _node;
                       _node = _node->_next;
                       return tmp;
               }
               Self& operator--()
               {
                       _node = _node->_prev;
                       return *this;
               }
               Self operator--(int)
               {
                       Self tmp = _node;
                       _node = _node->_prev;
                       return tmp;
               }
        };
        template<class T>
        class list
        {
               typedef _list_node<T> Node; //重复typedef是因为上一个typedef只有在所在的{}内才有效
        public:
                //开始的框架:typedef _list_iterator<T> iterator;
               typedef _list_iterator<T,T&,T*> iterator;
               typedef _list_iterator<T, const T&, const T*> const_iterator; //类模板给不同的参数,会变成不同的类型
                //关于为什么const迭代器中T的前面不用加const:加上const,迭代器中的T就表示const T,_list_node<T>就表示
               //_list_node<const T>list存储的数据变成不可修改的了,而Node中的内容是可以修改的。类型不匹配,
               //在类模板中不同类型之间是不能赋值的(有const和没有const是不同类型),会报错。
               //迭代器封装以后,在使用时(不同容器的迭代器)看起来是一样的。如果没有被public修饰,就无法在list外使用。
               //T是存储的数据类型,T&/const T&是返回存储的数据,加引用是为了减少拷贝,
               //有无const取决于可不可以通过迭代器修改数据。
//关于迭代器使用的图示:
               list()
               {
                       _head = new Node;
                       _head->_next = _head;
                       _head->_prev = _head;
               }
                //传统写法:
               //list(const list<T>& it)//it2(it1)
               //{
               //      _head = new Node;
               //      _head->_next = _head;
               //      _head->_prev = _head;
               //      for (auto e : it)
               //      {
               //             push_back(e);
               //      }
               //}
               //现代写法:
               void empty_init() //库中也提供了这个函数
               {
                       _head = new Node;
                       _head->_next = _head;
                       _head->_prev = _head;
               }
               template <class InputIterator>
               list(InputIterator first, InputIterator last)
               {
                       empty_init();
                       while (first != last)
                       {
                              push_back(*first);
                              first++;
                       }
               }
               void swap(list<T>& it) //it的内容要修改,不能加const
               {
                       std::swap(_head, it._head);
                        //依据实现size()的方式,可能还有交换size,这里没有实现size(),所以没交换。
               }
               list(const list<T>& it)
               {
                       empty_init(); //不能用未初始化的list和tmp交换。
                       list<T> tmp(it.begin(), it.end());
                       swap(tmp);
               }
               list<T>& operator=(list<T> it) //it2 = it1
               {
                       swap(it); //传值拷贝,it中已经有需要的数据了
                       return *this;
               }
               ~list()
               {
                       clear();
                       delete _head;
                       _head = nullptr;
               }
               void clear()
               {
                       iterator it = begin();
                       while (it != end())
                       {
                              it = erase(it);
                       }
               }
               void push_back(const T& x)
               {
                        ///*Node* tmp = new Node(x);
                       //_head->_prev->_next = tmp;
                       //tmp->_prev = _head->_prev;
                       //tmp->_next = _head;
                       //_head->_prev = tmp;*/
                       //Node* tmp = new Node(x);
                       //Node* tail = _head->_prev;//保存尾节点的地址,就可以不用按顺序完成链表的连接了
                       //tail->_next = tmp;
                       //tmp->_prev = tail;
                       //tmp->_next = _head;
                       //_head->_prev = tmp;
                       insert(end(), x);
               }
               void push_front(const T& x)
               {
                       insert(begin(), x);
               }
               void pop_back()
               {
                       erase(--end()); //这里可以用--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);
               }
               iterator insert(iterator pos, const T& val) //在pos位置之前插入数据
               {
                       Node* newnode = new Node(val);
                       Node* cur = pos._node;
                       cur->_prev->_next = newnode;
                       newnode->_prev = cur->_prev;
                       newnode->_next = cur;
                       cur->_prev = newnode;
                       return iterator(newnode); //insert的返回值很少用,和库里尽量保持一致还是加上了。
               }
               iterator erase(iterator 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); //返回值解决迭代器失效的问题
               }
        private:
               Node* _head;
        };
}
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
#include<list>
void test_list1()
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        bit::list<int>::iterator it = l.begin();
        while (it != l.end())
        {
               cout << *it << " ";
               ++it;
        }
}
struct AA
{
        AA(int a = 0, int b = 0)
        {
               a1 = a;
               a2 = b;
        }
        int a1;
        int a2;
};
cout没有AA的重载的解决方案一:加上重载
//ostream& operator<<(ostream& out, AA& aa)//函数中不能定义函数
//{
//      cout << aa.a1 << " " << aa.a2 << " ";
//      return out;
//}
void test_list2()
{
        bit::list<AA> l;
        l.push_back(AA(1, 1));
        l.push_back(AA(2, 2));
        l.push_back(AA(3, 3));
        l.push_back(AA(4, 4));
        l.push_back(AA(5, 5));
        bit::list<AA>::iterator it = l.begin();
        /*while (it != l.end())
        {
               cout << *it << " ";//这里的it是迭代器,解引用后拿到的是_date,_date的类型是T,即AA。cout没有AA的重载
               ++it;
        }*/
        while (it != l.end())
        {
                cout没有AA的重载的解决方案二:
               //cout << (*it).a1 << " " << (*it).a2 << " ";
               //it是迭代器,迭代器模仿的是指针,指针有通过->直接访问数据的功能,而it是一个类
               //cout << it->a1 << " " << it->a2 << " ";//原本应该这样使用的:it._node->_date.a1
               //库中支持迭代器直接访问数据,库中函数:T* operator->() const { return &(operator*()); }
               //it调用operator*(),*it会拿到其中的数据,即拿到_next->date,为operator*()的返回值;
               //it调用operator->()返回&(_node->date),即AA*,it->a1即AA* a1,实际上编译器为了可读性对这里进行简化。
               //it->a1实际上应该是it->->a1。it->即:it.operator->()返回AA*,AA*->a1,读取到数据
               cout << it->a1 << " " << it->a2 << " ";
               ++it;
        }
}
void print_list(const bit::list<int> tmp)
{
        bit::list<int>::const_iterator it = tmp.begin();
         //如果只是在begin()后加上const的修饰,只能编译通过,不能保证数据不可修改
        //要能做到不可修改,需要给 * 和 -> 的重载返回值加上const,但是加上const同时也意味着非const修饰的迭代器不可修改
        // 一般情况下,会选择再写一个_list_const_iterator类型的类,但是和_list_iterator类的逻辑高度重合,维护起来不方便
        // 下面跳转到list类部分,见证大佬为何是大佬。(每个注释有开始的框架的上下行都是大佬智慧的结晶)
        // 还不理解的翻到末尾,有图解说明
        //标准库中begin()可以自动识别迭代器类型,但是不能将const修饰的迭代器赋值给非const修饰的迭代器。
        //自定义类型被const修饰和不被const修饰是两个无关类型,
        //不能将被const修饰的类型赋值给不被const修饰的类型,标准库中也一样
        //比如说自定义类型的迭代器,不能说用const修饰了,就能支持const类型运算,还要有const类型对应运算的重载,
        //就算不被const修饰类型已经有对应的运算重载也不行。
        while (it != tmp.end())
        {
               cout << *it << " ";
               ++it;
        }
}
void test_list3()
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        print_list(l);
}
void test_list4()
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        l.insert(l.begin(), 10);
        l.push_front(20);
        l.push_front(30);
        l.push_front(40);
        l.erase(l.begin());
        l.pop_back();
        l.pop_front();
        bit::list<int>::iterator it = l.begin();
        while (it != l.end())
        {
               cout << *it << " ";
               ++it;
        }
}
void test_list5() //关于list的insert的迭代器失效问题
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        l.push_back(6);
        bit::list<int>::iterator it = l.begin();
        while (it != l.end())
        {
               if (*it % 2 == 0)
               {
                       l.insert(it, *it * 10);
               }
               it++;
        }
        for (auto it : l)
        {
               cout << it << " ";
        }
        cout << endl; //运行发现没什么问题
        //原因在于:list空间不连续,insert插入数据不需要挪动数据,所以不存在野指针问题,
        //insert插入数据,插入在指向的节点之前,意义也没有发生变化。
}
void test_list6() //关于list的erase的迭代器失效问题
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        l.push_back(6);
        bit::list<int>::iterator it = l.begin();
         /*while (it != l.end())
        {
               if (*it % 2 == 0)
               {
                       l.erase(it);//这样写是必然崩溃的,it都被释放了,it迭代器自然失效
               }
               it++;
        }*/
        while (it != l.end())
        {
               if (*it % 2 == 0)
               {
                       it = l.erase(it);
               }
               else
               {
                       it++;
               }
        }
        for (auto it : l)
        {
               cout << it << " ";
        }
        cout << endl;
        l.clear();
        l.push_back(1);
        l.push_back(2);
        l.push_back(3);
        for (auto it : l)
        {
               cout << it << " ";
        }
        cout << endl;
}
void test_list7()
{
        bit::list<int> l;
        l.push_back(1);
        l.push_back(2);
        l.push_back(3);
        l.push_back(4);
        l.push_back(5);
        l.push_back(6);
        bit::list<int> l2(l);
        for (auto it : l)
        {
               cout << it << " ";
        }
        cout << endl;
        l.pop_back();
        l2 = l;
        for (auto it : l2)
        {
               cout << it << " ";
        }
}
int main()
{
        test_list7();
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值