(C++) list底层模拟实现

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

 个人主页:Lei宝啊 

愿所有美好如期而遇


首先,list底层是一个带头双向循环链表,再一个,我们还要解决一个问题,list的迭代器,vector和string的迭代器可以直接++,是因为他们的地址空间是连续的,而链表不是,所以链表的迭代器封装的不是原生指针,我们需要想办法解决这个问题。

我们要解决list<T>::iterator可以++,既然我们不能封装原生指针,那么我们就对他进行运算符重载,但是在我们模拟的list类里,是不应该有这样的用法:list<int> lt, lt.operater++(),我们想要的是迭代器的运算符重载,即: list<int>::iterator it = lt.begin(); it++  这样的用法,那么我们就写一个迭代器的类,在这个类里重载++运算符。

首先是链表,我们先写出带头双向循环链表的结构,以及他的构造函数,至于为什么不用class类,是因为这样做后续可直接使用他的成员变量,如果写成类,还需要get  set函数,比较麻烦,我们模拟实现理解原理即可,所以为了省事,不使用class。

template<class T>
struct ListNode
{
	ListNode<T>* next;
	ListNode<T>* prev;
	T data;

	ListNode(const T& x = T())
		:next(nullptr)
		,prev(nullptr)
		,data(x)
	{}
};

接下来是list类的实现,第一个typedef是将链表的类型重命名,第二个typedef是将迭代器类进行重命名,第三个typedef也是将迭代器类进行重命名,并且这两个迭代器是为了区分const和非const,模版参数我们也可以看得出来。

template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef __list_iterator<T, T&, T*> iterator;
	typedef __list_iterator<T, const T&, const T*> const_iterator;

    //empty_init方法是初始化一个哨兵位, 接着就是构造函数,也就是初始化一个哨兵位;
	void empty_init()
	{
		_head = new Node();
		_head->prev = _head;
		_head->next = _head;
	}

	//构造函数
	//list<int> lt(); &lt  this
	list<T>()
	{
		empty_init();
	}

	void swap(list<T>& x)
	{
		std::swap(_head, x._head);
	}

	//拷贝构造
    //对于拷贝构造,我们需要进行深拷贝,先初始化一个哨兵位,然后遍历被拷贝的链表,             
    //用push_back方 法不断尾插节点,完成深拷贝;
	list(list<T>& copy)
	{			 
		empty_init();

		iterator it = copy.begin();
		while (it != copy.end())
		{
			push_back(it._node->data);
			it++;
		}
	}

    //最后就是赋值运算符重载,我们传参不传引用,就是为了在传参时拷贝构造出一个链表,           
    //然后我们只需要交换_head,就可以完成赋值,这里我们写一个swap方法。
	list<T>& operator=(list<T> tmp)
	{
		swap(tmp);
		return *this;
	}
    
    //push_back方法,只需要理清楚插入节点newnode和_head的关系就好;
	void push_back(const T& x = T())
	{
		//_head->prev newnode _head

		//Node* newnode = new Node(x);
		//Node* tail = _head->prev;
		//
		//tail->next = newnode;
		//newnode->prev = tail;
		//_head->prev = newnode;
		//newnode->next = _head;
		insert(end(), x);

	}

    //insert方法也是类似于push_back方法,我们写出insert方法后,                     
    //push_back完全可以复用insert方法,链表的insert没有迭代器失效问题;
	iterator insert(iterator pos, const T& x = T())
	{
		//pos->prev newnode pos
		Node* cur = pos._node;
		Node* prev = cur->prev;
	
		//prev newnode cur
		Node* newnode = new Node(x);
		newnode->next = cur;
		cur->prev = newnode;
		prev->next = newnode;
		newnode->prev = prev;

		return newnode;
	}

    //begin和end方法,也就是返回链表的开始位置和尾部位置,注意,                             
    //开始位置是_head->next,尾部位置是_head->prev;
	iterator begin()
	{
		return iterator(_head->next);
	}

	const_iterator begin() const
	{
		return _head->next;
	}

	iterator end()
	{
		return iterator(_head);
	}

	const_iterator end() const
	{
		return _head;
	}

    //erase方法我们使用时还是要注意迭代器失效问题,删除一个节点后,                          
    //迭代器就是一个野指针,也就失效了;
	iterator erase(iterator pos)
	{
		Node* cur = pos._node;
		Node* prev = cur->prev;
		Node* next = cur->next;

		prev->next = next;
		next->prev = prev;

		return iterator(next);
	}

    //clear方法,我们需要遍历链表释放所有节点,我们这里可以复用erase方法;
	void clear()
	{
		iterator it= begin();
		while (it != end())
		{
			erase(it++);
		}
	}

    //析构函数,我们调用clear释放掉其他节点后,我们再单独释放掉哨兵位;
	~list()
	{
		clear();

		delete _head;
		_head = nullptr;
	}

private:
	Node* _head;

};

__list_iterator类的实现

模版参数有三个,因为我们要明白,iterator是有解引用操作的,非const对象可以对值修改,但是const对象不可以,当然,我们可以写他的重载,但是有了这样一个模板参数,只需要修改返回值即可,并且我们重载了->运算符,这个返回值时一个指针,我们仍然需要区分是否是const对象。

template<class T, class ref, class ptr>
struct __list_iterator
{
	typedef ListNode<T> Node;
	
public:

	//构造函数
	__list_iterator(Node* node)
		:_node(node)
	{}

	//前置++
	__list_iterator& operator++()
	{
		_node = _node->next;
		return *this;
	}

	//后置++
	__list_iterator& operator++(int)
	{
		//auto tmp = __list_iterator(this->_node);
		auto tmp = __list_iterator(*this);
		_node = _node->next;
		return tmp;
	}

	//前置--
	__list_iterator& operator--()
	{
		_node = _node->prev;
		return *this;
	}

	//后置--
	__list_iterator& operator--(int)
	{
		//auto tmp = __list_iterator(this->_node);
		auto tmp = __list_iterator(*this);
		_node = _node->prev;
		return tmp;
	}

	ref operator*()
	{
		return _node->data;
	}

	ptr operator->()
	{
		return &_node->data;
	}

	bool operator!=(const __list_iterator& x)
	{
		return _node != x._node;
	}
	
	Node* _node;
};

那么我们是如何使用的呢?

list<int>::iterator it = lt.begin(),lt是list<int>类型,非const,则调用非const begin方法,但是我们的begin返回值是_head->next,是个Node*类型的指针,我们返回值类型可是iterator,封装的是__list_iterator类,但是这个类的构造函数所传的参数是单参数,而且也是Node*,也就是说,会进行隐式类型转换,_head->next会构造出一个__list_iterator临时对象,然后进行返回,然后这个临时对象再拷贝构造it,如果编译器进行优化,也就变成了直接进行拷贝构造。

我们的主要问题,对++运算符的重载也就不是问题了,唯一需要注意的是前置和后置++,区分它们只能是通过参数了,我们一般是给后置++的参数加上int。

最后一个问题就是->的重载,如果我们使用是不是这样使用:

假设我们有一个结构体,AA{int a; int b},list<AA> lt; 我们要通过iterator访问他的成员变量,list<AA>::iterator it = lt.begin(),返回值就是AA的地址,那么我们如何访问他的成员变量呢?

it.operator->()->a,或者是it->->a?(C++) list底层模拟实现,C++,c++,开发语言

编译器省略了一个->,所以我们使用时直接it->a即可

(C++) list底层模拟实现,C++,c++,开发语言 文章来源地址https://www.toymoban.com/news/detail-810868.html

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

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

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

相关文章

  • 【C++】list的使用及底层实现原理

      本篇文章对list的使用进行了举例讲解。同时也对底层实现进行了讲解。底层的实现关键在于迭代器的实现。希望本篇文章会对你有所帮助。 文章目录 一、list的使用 1、1 list的介绍 1、2 list的使用 1、2、1 list的常规使用  1、2、2 list的sort讲解 二、list的底层实现 2、1 初构l

    2024年02月16日
    浏览(26)
  • 【C++杂货铺】探索list的底层实现

    list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list 的底层是双向链表结构,双向链表中的每个元素存储在互不相关的独立节点中,在节点中通过指针指向的前一个元素和后一个元素。 list 和 forward_list 非常相似:最主要的不

    2024年02月09日
    浏览(26)
  • 模拟ArrayList(顺序表)的底层实现

    2024年02月16日
    浏览(30)
  • 【C++模拟实现】list的模拟实现

    作者:爱写代码的刚子 时间:2023.9.3 前言:本篇博客关于list的模拟实现和模拟实现中遇到的问题 list模拟实现的部分代码 list模拟实现中的要点 const_iterator的实现 我们选择使用模版参数,复用iterator的类,设置三个模版参数: templateclass T,class Ref,class Ptr 并且 typedef __list_iter

    2024年02月09日
    浏览(40)
  • C++ stl容器vector的底层模拟实现

    目录 前言:   1.成员变量,容量与大小 2.构造函数 无参构造: 带参的使用值进行构造:  使用迭代器区间进行构造: 3.交换 4.拷贝构造 5.赋值重载 6.迭代器 7.扩容 reserve: resize: 8.插入与删除 insert: erase: insert迭代器失效问题: erase迭代器失效问题: 9.头插头删 10.[]重载

    2024年04月15日
    浏览(31)
  • C++ stl容器string的底层模拟实现

    目录 前言: 1.成员变量 2.构造函数与拷贝构造函数 3.析构函数 4.赋值重载 5.[]重载 6.比较关系重载 7.reserve 8.resize 9.push_back,append和重载+= 10.insert 11.erase 12.find 14.迭代器 15.流插入,流提取重载 16.swap 17.c_str 18.完整代码+测试 总结: 1.成员变量 首先注意的就是_str,不能是const类型

    2024年04月23日
    浏览(29)
  • [C++历练之路]vector的介绍以及底层模拟实现

    W...Y的主页 😊 代码仓库分享 💕 🍔前言: 我们学习了STL中的string以及其所有重要接口并进行了模拟实现,但是STL中包含的内容不止于此。学习了string之后继续学习STL中的vector,学习成本会大大降低,因为他们非现类似,现在就让我们进入vector的世界中吧! 目录 vector的介绍

    2024年02月04日
    浏览(33)
  • 【C++】深度剖析string类的底层结构及其模拟实现

    在上两篇中,我们已经学习了string类的一个使用,并且做了一些相关的OJ练习,相信大家现在对于string的使用已经没什么问题了。 那我们这篇文章呢,就来带大家对string进行一个模拟实现,这篇文章过后,有些地方大家或许就可以理解的更深刻一点。 那通过之前文章的学习我

    2023年04月17日
    浏览(89)
  • C++ list模拟实现

    源码中的list实现为 带头双向链表   list类的对象有两个成员:指向头结点的指针_head,统计数据个数的_size 在模拟实现list之前,需要先模拟实现 结点类,迭代器类 结点类:三个成员,_data _prev _next , 实现成struct (也是类,不过与class不同的是,它的成员都是公开的,都可以

    2024年02月11日
    浏览(35)
  • list的模拟实现

    前言         list 是 STL 中重要的容器,了解它的原理对于我们掌握它是有很多的帮助的,一般 list 和 vector 都是一起来使用的,因为它们的优缺点不同,刚好可以互补。list的优点是任意位置的插入和删除都很快,它的缺点是不支持随机访问,而vector的优点是支持随机访问,

    2024年02月09日
    浏览(21)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包