C++初阶—list深度解剖及模拟实现

这篇具有很好参考价值的文章主要介绍了C++初阶—list深度解剖及模拟实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++初阶—list深度解剖及模拟实现

 

目录

➡️0. 前言

😊1.简易框架实现

🐔1. list和__list_node分析实现

🐔2. 无参构造

😊2.迭代器实现

🐔1. list普通迭代器面临问题及解决方案

🐔2. __list_node\iterator\list三类分析

🐔3. list中const迭代器面临问题及解决方案

🐔4. list中模板参数为自定义类型迭代器优化

🐔5. list迭代器的完整实现

😊3.其他成员属性的实现

🐔1. push_back()的实现

🐔2. 迭代器区间构造实现

🐔3. 拷贝构造及赋值的现代化方法实现(深浅拷贝参考vector)

🐔4. insert()的实现

🐔5. erase()的实现

🐔6. 析构的实现


➡️0. 前言

list 容器,又称双向链表容器,即该容器的底层是以带头双向循环链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。

list链表是序列容器,允许在序列内的任何位置进行常量时间的插入和删除操作,以及两个方向的迭代。

本篇文章暂时不讲解拟制迭代器,将后续进行讲解!

list单独提供了merge归并排序是通过与前面元素的链接和后面元素的链接的每个元素的关联在内部保持的。它们与forward_list非常相似:主要区别在于forward_list对象是单链表,因此它们只能向前迭代,以换取更小和更高效。与其他基本标准序列容器(array、vector和deque)相比,列表在容器内的任何位置插入、提取和移动元素(迭代器已经获得)方面通常表现更好,因此在大量使用列表的算法(如排序算法)中也表现更好。与其他序列容器相比,列表和forward_lists的主要缺点是它们无法通过位置直接访问元素;例如,要访问列表中的第六个元素,必须从所有已知位置(如开始或结束)迭代到该位置,这需要在这些位置之间的距离上花费线性时间。它们还会消耗一些额外的内存来保存与每个元素相关联的链接信息(对于包含小型元素的大型列表来说,这可能是一个重要因素)。

😊1.简易框架实现

C++初阶—list深度解剖及模拟实现

🐔1. list和__list_node分析实现

 list容器成员是一个结构体__list_node变量的指针,指向链表的头节点,而结构体__list_node便是链表的结点指向堆区的结构体,包含了模板数据类型的数据,以及前后指针,指向下一个节点!

C++一般很少采用内部类的方式实现,因此再实现list容器时,需要先实现struct __list_node结构体,以及list类,结构体内所有成员变量及成员属性皆为public,便于在list类中访问节点数据

namespace Thb {
    template<class T> struct __list_node {
        T _data;
        __list_node<T>* _next;
        __list_node<T>* _prev;
    };

    template<class T> class list {
        typedef __list_node<T> list_node;
    public:

    private:
        list_node* _head;
    };
}

🐔2. 无参构造

list为双向带头循环链表,因此在实例化list时,需要先给定头节点,且头节点的_next及_prev都指向自己!而给定头节点,需要 new 一个__list_node对象,因此对两个类都提供无参构造!

1、__list_node的无参构造,采用缺省参数构造,如果有数据,直接将数据初始化

        list_node(const T& x = T()) 
            :_data(x)
            ,_next(nullptr)
            ,_prev(nullptr)
        {
        }

2、list初始化,开辟头节点

    public:
        typedef __list_node<T> list_node;
        void empty_init() {
            _head = new list_node;
            _head->_next = _head;
            _head->_prev = _head;
        }
    private:
        list()
        {
            empty_init();
        }

😊2.迭代器实现

🐔1. list普通迭代器面临问题及解决方案

面临问题:

由于list和string、vector存储方式不同,list不是连续的物理空间,因此在实现迭代器时,++并不会跳转到下一个节点的位置,同时解引用操作,也不会获取到数据。

解决方案:

为了实现list非连续物理空间的容器,可以对其迭代器实现为一个对象,调用begin,end等成员方法时,返回一个迭代器对象,而对其所属域的迭代器对象内的操作符及运算符进行重写

    template<class T>
    struct __list_iterator {
            typedef __list_node<T> list_node;
            typedef __list_iterator<T> iterator;

            list_node* _node;

            __list_iterator(list_node* node)
                :_node(node)
            {}
            bool operator!=(const iterator& it) const {
                return _node != it._node;
            }

            iterator& operator++() {
                _node = _node->_next;
                return *this;
            }
            iterator operator++(int) {
                iterator temp(*this);
                _node = _node->_next;
                return *this;
            }

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

上述迭代器类的大致实现,对迭代器的普遍用法 != 、前置 ++ 、--、后置 ++、--、以及解引用 * 访问数据进行了实现,虽然可以解决普通迭代的问题,但仍然具有很多方面的不足。

🐔2. __list_node\iterator\list三类分析

C++初阶—list深度解剖及模拟实现

三个类是如何协调调用?为何iterator默认函数满足使用?

  1. 可通过代码观察迭代器未在堆区开辟空间,同时使用了list的节点进行构造,因此不需要采用delete析构节点,默认即可,否则源数据空间将会遭到破坏!
  2. 传递的节点皆是通过指针传递,因此可以访问到底层的数据!
  3. 尽管iterator可以访问底层数据,但是节点全部都被封装在list里,没有list传递节点,iterator是无法直接访问底层数据的,因此可以体现c++的封装特性!
  4. list迭代器并不涉及深拷贝问题,而只是获取一个节点的指针,重写++、--等便捷访问前后节点数据,因此使用默认赋值,及默认拷贝通过list传递地址即可!

🐔3. list中const迭代器面临问题及解决方案

面临问题:

虽然上述解决了普通迭代器所面临的问题,但是对于const对象,无法解决一个类模板所实现的迭代器对象,仅仅根据返回值的不同,无法构成函数重载!!!

C++初阶—list深度解剖及模拟实现

 解决方法:

  • 第一种解决方案:重新实现一个对象__const_iterator,复写所有方法,返回const T&(pass原因:不符合高内聚,低耦合。重复代码太多,且累赘!)
  • 第二种解决方案:此时就轮到类模板参数发挥作用了,C++模板可以提供多个模板参数根据参数不同,模板实例化对象便是不同类型对象,而上述方法类模板参数为一个,因此每次使用list在使用iterator实例化的对象都是同类型对象,无法区分!

虽然上述方法,可以很好实现解耦,但是类模板还是不够完善,仍然面临的一些问题!

🐔4. list中模板参数为自定义类型迭代器优化

当list模板参数为自定义类型时,如下代码:

class Point {
public:
	Point() {

	}
	Point(int x, int y) :_x(x), _y(y) {
	}
	int getx() {
		return _x;
	}
	int gety() {
		return _y;
	}
private:
	int _x;
	int _y;
};

void testPoint() {
	Thb::list<Point> ls;
	ls.push_back(Point(1, 2));
	ls.push_back(Point(2, 3));
	ls.push_back(Point(3, 4));
	ls.push_back(Point(4, 5));
	auto it = ls.begin();
	while (it != ls.end())
	{
		cout << "ponit:" << (*it).getx() << "/" << (*it).gety() << endl;
		it++;
	}
}

此时迭代器要想调用类模板对象public方法和属性,只能先解引用获取到对象,再通过 . 的方式才能进行调用,不便于操作!

因此迭代器需要实现->可以直接进行调用,而如何重写 -> ,就需要分析c++特性,c++对于重写operator->时有特殊的规定:语法为了可读性,编译器对->进行特殊处理,重写时返回值再次使用->,即省略了一个->,相当于返回模板类型对象的地址!

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

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



/



void testPoint() {
	Thb::list<Point> ls;
	ls.push_back(Point(1, 2));
	ls.push_back(Point(2, 3));
	ls.push_back(Point(3, 4));
	ls.push_back(Point(4, 5));
	auto it = ls.begin();
	while (it != ls.end())
	{
		cout << "ponit:" << it->getx() << "/" << t->getx() << endl;
		it++;
	}
}

分析代码先通过重写的 this -> operator*(); 获取node节点的_data,便是获取模板Point实例化对象,再通过取地址&Point,获取实例化数据对象的地址,此时返回point实例对象的地址,便可以再次通过->访问类成员方法,在实际测试中可看出c++省略了一个->。

可以通过迭代器的完善观察,解引用 * 和 -> 访问操作符皆涉及到然会模板数据,当返回类型为 const T* 或者 const T& 时,便说明其是一个 const_iterator ,因此需要增加两个类模板参数!!!

🐔5. list迭代器的完整实现

新增Ref(reference引用),Ptr(pointer指针)两个模板参数,并对其成员函数返回值做出更改,对应模板传类型,而list中

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

展示了类模板传递不同,所实例化不同类型的迭代器对象,进行解耦!

    template<class T, class Ref, class Ptr>
    struct __list_iterator {
            typedef __list_node<T> list_node;
            typedef __list_iterator<T, Ref, Ptr> iterator;

            list_node* _node;

            __list_iterator(list_node* node)
                :_node(node)
            {}
            bool operator!=(const iterator& it) const {
                return _node != it._node;
            }
            bool operator==(const iterator& it) const {
                return _node == it._node;
            }

            iterator& operator++() {
                _node = _node->_next;
                return *this;
            }
            iterator operator++(int) {
                iterator temp(*this);
                _node = _node->_next;
                return *this;
            }

            iterator& operator--() {
                _node = _node->_prev;
                return *this;
            }
            iterator operator--(int) {
                iterator temp(*this);
                _node = _node->_prev;
                return *this;
            }

            //T* operator->() {
            Ptr operator->() const {
                return &(operator*());
            }
            //T& operator*() const {
            Ref operator*() const {
                return _node->_data;
            }
     };

    template<class T> 
    class list {
        typedef __list_node<T> list_node;
        void empty_init() {
            _head = new list_node;
            _head->_next = _head;
            _head->_prev = _head;
        }
    public:
        typedef __list_iterator<T, T&, T*> iterator;
        typedef __list_iterator<T, const T&, const T*> const_iterator;

        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);
        }
        //默认函数                                                                                                         
        list()
        {
            empty_init();
        }

    private:
        list_node* _head;
    };
}

😊3.其他成员属性的实现

🐔1. push_back()的实现

        void push_back(const T& value) {
            list_node* tail = _head->_prev;
            list_node* newnode = new list_node(value);
            newnode->_next = _head;
            newnode->_prev = tail;
            _head->_prev = newnode;
            tail->_next = newnode;
        }

🐔2. 迭代器区间构造实现

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

🐔3. 拷贝构造及赋值的现代化方法实现(深浅拷贝参考vector)

        list(const list<T>& ls) 
        {
            empty_init();
            list<T> temp(ls.begin(), ls.end());
            swap(temp);
        }
        list<T>& operator=(list<T> ls) {
            swap(ls);
            return *this;
        }
        void swap(list<T>& ls) {
            std::swap(_head, ls._head);
        }

🐔4. insert()的实现

        iterator insert(iterator pos, const T& x) {
            list_node* newnode = new list_node(x);
            list_node* pos_node = pos._node;
            newnode->_next = pos_node;
            newnode->_prev = pos_node->_prev;
            pos_node->_prev->_next = newnode;
            pos_node->_prev = newnode;
            return iterator(newnode);
        }

🐔5. erase()的实现

        iterator erase(iterator pos) {
            assert(pos != end());
            list_node* dele = pos._node;
            list_node* next = dele->_next;
            next->_prev = dele->_prev;
            dele->_prev->_next = next;
            delete dele;
            dele = nullptr;
            return iterator(next);
        }

🐔6. 析构的实现

        void clear() {
            iterator it = begin();
            while (it != end()) {
                it = erase(it);
            }
        }
        ~list() {
            clear();
            delete _head;
        }

析构函数复用了clear,clear删除除哨兵位头节点的所有节点!而clear通过复用erase保证删除节点时,其逻辑关系仍然稳定不变!同时使用迭代器,begin记录了头节点的下一个位置,erase返回删除数据的下一个数据的位置的迭代器,进行迭代器it的更新!文章来源地址https://www.toymoban.com/news/detail-488891.html

到了这里,关于C++初阶—list深度解剖及模拟实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++初阶之一篇文章教会你list(模拟实现)

    成员类型表 这个表中列出了C++标准库中list容器的一些成员类型定义。这些类型定义是为了使list能够与C++标准库的其他组件协同工作,并提供一些通用的标准接口。每个成员类型的用处: value_type : 这个成员类型代表list容器中存储的数据类型,即模板参数T的类型。 allocator_

    2024年02月12日
    浏览(21)
  • 【C++】STL——list深度剖析 及 模拟实现

    这篇文章我们来继续STL的学习,今天我们要学习的是list,也是STL中容器的一员。 和之前一样,我们还是先学习它的使用,然后再对它进行一个深度剖析和模拟实现。 1.1 list的介绍 list的文档介绍 list的底层实现其实就是我们之前数据结构学过的带头双向循环链表: 1.2 list的使

    2024年02月05日
    浏览(29)
  • 【C++】STL之list深度剖析及模拟实现

    目录 前言 一、list 的使用  1、构造函数 2、迭代器 3、增删查改 4、其他函数使用 二、list 的模拟实现  1、节点的创建  2、push_back 和 push_front  3、普通迭代器  4、const 迭代器  5、增删查改(insert、erase、pop_back、pop_front)  6、构造函数和析构函数   6.1、默认构造   6.2、构造

    2024年02月07日
    浏览(29)
  • 【C++从入门到放弃】list深度剖析及模拟实现

    🧑‍💻作者: @情话0.0 📝专栏:《C++从入门到放弃》 👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢! list 是允许在序列内的任何位置进行 常量时间的插入和删除 操作的序列容器,并且该容器可以前后双向迭代。 list 的底

    2024年02月10日
    浏览(33)
  • C++入门之stl六大组件--List源码深度剖析及模拟实现

    文章目录 前言 一、List源码阅读 二、List常用接口模拟实现 1.定义一个list节点 2.实现一个迭代器 2.2const迭代器 3.定义一个链表,以及实现链表的常用接口 三、List和Vector 总结 本文中出现的模拟实现经过本地vs测试无误,文件已上传gitee,地址:list: 模仿实现stl的list - Gitee.com 首

    2024年02月13日
    浏览(34)
  • 【C++】深度解剖多态

    作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等 座右铭:松树千年终是朽,槿花一日自为荣。 目标:了解什么是多态,熟练掌握多态的定义,熟读抽象类。 毒鸡汤:一半明媚,一半阴霾,这就是人生 望小伙伴们点赞👍收藏✨加关注哟💕💕  前面我们学习了继

    2024年03月26日
    浏览(33)
  • 【C++初阶9-list实现】封装——我主沉浮,何不能至?

    翻阅文档即可。 带头双向循环链表。 *数据结构初阶已经实现过,这里只是变成类和对象的版本 list的实现,价值最大的不是这里,而是迭代器…… 封装,我主沉浮,何不能至? 封装给了我们很灵活的操作空间。为什么这么说? 迭代器是一种类的内嵌类型,具有指针行为,

    2023年04月27日
    浏览(24)
  • C++初阶-vector类的模拟实现

    C++ STL中的vector就类似于C语言当中的数组,但是vector又拥有很多数组没有的接口,使用起来更加方便。 相比于STL中的string,vector可以定义不同的数据类型。 迭代器的本质可以暂时看作是指针,模拟实现vector,需要定义三个指针:指向起始位置_start,指向最后一个有效元素的下

    2024年02月04日
    浏览(136)
  • 【C++初阶】9. string类的模拟实现

    string类的完整实现放这里啦!快来看看吧 string类的作用就是将字符串类型实现更多功能,运算符重载,增删改查等等操作,所以其成员就包含char*的字符串 在之前的学习过程中,我们了解到类中存在的六个默认函数,其中就包含默认构造函数,那么对于string类是否需要用户自

    2024年02月09日
    浏览(26)
  • 【C++初阶】学习string类的模拟实现

    前面已经学习了string类的用法,这篇文章将更深入的学习string类,了解string类的底层是怎么实现的。当然,这里只是模拟一些常用的,不常用的可以看文档学习。 我们一共创建两个文件,一个是test.cpp文件,用于测试;另一个是string.h文件,用于声明和定义要模拟的string类。

    2024年02月03日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包