C++——list常见函数的使用和模拟实现(2)

        在list的上一篇博客里实现了list基本的初始化、插入数据、删除数据的基本功能,这些功能的实现方式只是在原先链表的实现里加入了模版而已,但是list作为一个容器,它还有一个基础的东西——迭代器。list的迭代器和之前实现的string和vector很大不同,这里就专门进行list迭代器基本功能的模拟实现。

        list的迭代器本质上是一个指向list结点的一个指针,但是因为list的结点在内存中的分布不是连续的,所以如果直接用一个原生的指针作为迭代器的话,在后续的操作中会有非常多的不同,所以我们把指针也封装成一个类,这样进行很多操作的时候就会更加方便。也是因为上面说的,list的结点在内存中的分布不是连续的,所以list迭代器并没有+= 、-=这样的操作。

        因为我们list本身就是一个模板类,所以它的迭代器也一定要是一个模板类才能够匹配上,迭代器的成员变量很简单,就是一个指向list结点的一个指针,所以在初始化的时候把这个某个结点赋值给成员变量即可。这个成员变量的类型一定是一个list结点的指针,我们后续的操作也是根据这个指针来的,所以这里我们typedef一下list_Node<T>,list_Node<T>就是list存储的数据的类型,方便后续的操作。

    template<class T>
    struct list_Iterator
    {
        typedef list_Node<T> Node;
        Node* _node;

        list_Iterator(Node* node)
	        :_node(node)
        {
        }
    }

        首先基本的就是对迭代器的解引用操作和->操作了。这个解引用操作符的重载很常规,只要返回这个内容的引用就行了,但是->操作符的重载却没有那么简单。因为本身我们的迭代器本身本质上就是一个指针,那么对这个指针用->那不是访问迭代器里的内容吗,但是->不应该要访问到内内容里的嘛,这样这个运算符重载不就改变了这个运算符的意义了吗。实际上在对迭代器使用->时,编译器是把xxx..operator->()->自动优化成了一个->,这样就能实现->原本的含义了,所以->返回的一个是一个结点数据的指针,因此才有了下面的写法。

   T& operator*()
    {
    	return _node->_data;
    }

    //iterator.operator->()->...
    T* operator->()
    {
	    return &(_node->_data);
    }

        ++和--操作的本质都是返回一个指向上一个或下一个结点的指针,也就是返回一个迭代器,为了方便操作,我们也可以typedef一下自身的类型。list本身就是一个双向带头循环列表,所以用迭代器指向的这个结点就能很轻松的找到前一个或后面一个结点。

    typedef list_Iterator<T> Self;
    Self& operator++()
    {
	    _node = _node->_next;
	    return *this;
    }

    Self& operator--()
    {
	    _node = _node->_prev;
	    return *this;
    }

        判断运算符的重载就更简单了,但是要注意的是,我们要判断的是两个结点的地址是否相同吗,不是比较结点存储的是否相同。

    bool operator!=(const Self& s) const
    {
	    return _node != s._node;
    }

    bool operator==(const Self& s) const
    {
	    return _node == s._node;
    }

        上面就完成了list迭代器的基本实现,但是如果这样写的话,我们会发现,如果要实现const迭代器的话,这样是不行的,因为如果我们只是简单的在iterator前加上一个const是不能实现我们的要求的,因为如果简单的用const修饰iterator的话,这样变成了iterator的指向不能改变,但是我们依旧可以修改iterator指向的那个结点保存的内容,和我们const_iterator的要求:指向可以改变,但是不能改变所指的内容的要求完全不符,所以不能直接这样简单的实现。

        同时我们在实现iterator的时候返回值也有所不同,这里就正好用模板就可以解决这个问题了,首先我们的const_iterator的解引用操作是->时不允许修改内容的,这里两种迭代器的不同需求我们可以通过模板解决,第一步就是在给迭代器的模板增加两个模板参数,其中Ref表示保存数据类型的引用,Ptr表示保存数据类型的指针。解引用操作符重载的返回值类型用Ref代替,->操作符重载的返回值用Ptr代替。

    template<class T,class Ref ,class Ptr>

        第二步,在list类的内部增加两个typedef,iterator就是一个正常的迭代器的类型,不过因为是上一点的原因我们要多加两个模板参数,而const_iterator的模板参数的类型则有变化,因为const_iterator的指向是可以改变的,所以要在模板参数里加上const,把const加在T之前,这样在迭代器里,使用解引用操作符和->操作符的时候就不能对保存的数据进行操作了,这样就实现了代码的复用。

    typedef list_Iterator<T,T&,T*> iterator;
    typedef list_Iterator<T,const T&,const T*> const_iterator;

引用和提供了关于实现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、付费专栏及课程。

余额充值