C++ stl容器list的底层模拟实现

目录

前言:

1.创建节点

2.普通迭代器的封

3.反向迭代器的封装

为什么要对正向迭代器进行封装?

4.const迭代器

5.构造函数

6.拷贝构造

7.赋值重载

8.insert

9.erase

10.析构

11.头插头删,尾插尾删

12.完整代码+简单测试

总结:


前言:

模拟实现list,本篇的重点就是由于list是一个双向循环链表结构,所以我们对迭代器的实现不能是简单的指针的++,--了,因为我们知道,链表的存储不一定是连续的,所以直接++,--是链接不起来节点的,所以我们要对迭代器也就是对节点的指针进行封装。结尾会附上完整的代码。

1.创建节点

	template<class T>
	struct list_node
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T _data;

		list_node(const T& x= T())//这里不给缺省值可能会因为没有默认构造函数而编不过
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};

注意给缺省值,这样全缺省就会被当做默认构造了,不会因为没有默认构造而报错。

我们实现的list是带哨兵位的,它同时是迭代器的end()(因为是双向循环的list)。

2.普通迭代器的封装

	template<class T,class Ref,class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> node;
		typedef _list_iterator<T, Ref, Ptr> self;

		node* _node;//对迭代器也就是节点的指针进行封装,因为list迭代器是不能直接++的
		_list_iterator(node* n)
			:_node(n)
		{}

		Ref operator*()//返回的必须是引用,不然改变不了外面的对象的成员,要支持对自己解引用改变值就要用应用
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &(_node->_data);//返回地址,再解引用直接访问数据
		}

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

		self operator++(int)
		{
			self tmp(*this);//默认的拷贝构造可以,因为没有深拷贝
			_node = _node->_next;
			return tmp;
		}

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

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

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

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

	};

注意list是双向迭代器,可以++,--,不能+,-

这里对迭代器的实现就如我们开始所说的, 迭代器的实现就是使用节点的指针实现的,而我们不能直接对list创建出的节点进行++,--,所以要进行一层封装;然后再对节点指针初始化。

重载解引用时要注意返回的是引用,不然对自己解引用的时候,返回值如果是临时的,是改变不了内部的data的。

对于箭头的解引用,是为了支持这样的场景:

struct AA
	{
		int _a1;
		int _a2;

		AA(int a1=0,int a2=0)
			:_a1(a1)
			,_a2(a2)
		{}
	};

	void test_list2()
	{
		list<AA> lt;
		lt.push_back(AA(1,1));
		lt.push_back(AA(2, 2));
		lt.push_back(AA(3, 3));

		list<AA>::iterator it = lt.begin();
		while (it != lt.end())
		{
			//cout << (*it)._a1 << " "<<(*it)._a2<<endl;
			cout << it->_a1 << " " << it->_a2 << endl;//面对这样的类型,需要重载->,.也可以访问,但是有点别扭

			++it;
		}
		cout << endl;
	}

迭代器遇到箭头,返回对象的地址也就是节点数据的地址,再解引用找到成员。或者说node中的data就是存放的是对象(也就是用来初始化的数据),然后重载的->拿到对象的地址,再->去访问里面的成员变量_a1。

对于前置后置++与--,前置就返回对象的引用,是传引用返回;后置需要进行拷贝给一个临时的对象,再对调用对象++--,返回的是tmp也就是没有改变的对象,是传值返回。注意区分前置后置,后置要加上参数int。

评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值