list(C++)的实现

目录

list介绍

list接口的实现

1.节点接口

2.迭代器接口的实现

迭代器的接口函数

3.list的实现

1.迭代器的实现

2.构造函数、拷贝构造、赋值重载

3.析构函数

4.insert

5.erase函数

6.clear函数

7.头插、尾删、、、、、、

8.size


list介绍

C++中的list是一个序列容器,它提供了双向链表的数据结构。不同于string、vector,它不能支持

随机访问,但是可以快速的在头尾、中间部分插入、删除数据。这就是list的优势。

在实现list时,重难点是迭代器iterator的实现。因为list是由一个个节点构成,不是顺序结构,而是链式结构。因此对iterator的++、--、* 等操作,都是对封装好的iterator类的进一次解剖而实现的

list接口的实现

list主要有三部分组成:节点接口、迭代器接口、list主体部分

1.节点接口

节点接口表示list<T>中的数据类型,双向链表一般是_date  _prev 和 _next三部分组成。

//节点
template <typename T>
struct list_node	//当希望全部成员public时,一般用struct
{
	T _date;
	list_node* _prev;
	list_node* _next;

	list_node(const T& x = T())		//节点构造函数,用来初始化节点
		:_date(x)
		,_prev(nullptr)
		,_next(nullptr)
	{}
};

由于我们需要在其它的类中经常需要使用节点,所以让成员对外全部开放。

全部成员都对外开放时(public)一般直接使用struct结构,而不是使用class结构。以此减少友元的使用

2.迭代器接口的实现

迭代器实施上被封装成为了单独的类,在类的内部完成了++  --  *  的操作。迭代器是借助一个节点:Node类型的指针完成的。

因此迭代器的实现包含:1.成员变量Node*指针 2.迭代器的功能函数

对于const迭代器和非const迭代器,我们采用模板的方式,进行统一实现。

// T T& T*
// T cosnt T& const T*

    template <typename T, typename Ref, typename Ptr>    //Ref 和 Ptr 可以指明迭代器类型
 

	template <typename T, typename Ref, typename Ptr>	//Ref 和 Ptr 可以指明迭代器类型
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;

		Node* _node;	//成员变量(Node完全公开)

_node是指向一个节点的指针,后续所有的工作都是借助这个指针完成的。

构造函数

内部就是用一个节点指针完成的构造

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

迭代器的构造是单参数的传参,因此后续使用迭代器时,可以出现隐式类型转换的情况。

迭代器的接口函数

由于迭代器分为const和非const版本,一般接口函数都是实现为函数重载,但是由于模板函数的实现,便可以剩下大部分实现。

template <typename T, typename Ref, typename Ptr>    //Ref 和 Ptr 可以指明迭代器类型

其中Ref就可以代表const T& 和 T&

Ptr就可以代表 const T* 和 T*

因此在实现接口函数时,只需要将返回值设置为Ref和Ptr就可以实现区分。

前置++、--

前置要求满足先++,后返回,因此在内部的实现中要符合这样的逻辑。

其次:++之后应该返回一个同类型的对象,因此设置返回值为迭代器的引用

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

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

后置++、--

后置要求先返回,后实现递增的操作。因此在返回时应该返回一个与当前对象一致的临时对象。

同时在形参传入(int)表示后置++


		self operator++(int)	//后置
		{
			self aa(*this);
			_node = _node->_next;
			return aa;
		}

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

*  --->

两个解引用操作符。

* : 对迭代器实现解引用时,返回的是内部的_date数据的引用。对于const迭代器,则数据不支持修改;非const迭代器,支持数据的修改。


		Ref operator*()		//Ref 是const T& 或者是 T&
		{
			return _node->_date;
		}

Ref作为引用,可以是const T& 也可以是 T&

-> : 内部返回的是数据的指针,同样const T* ,也可以是 T*

Ptr operator->()
{
	return &(_node->_date);
}

((在实际的使用中,应该是it.operator->()->这两个部分组成。第一个箭头部分返回原生指针,第二个箭头部分再将原生指针采用->解引用。

但是为了调用方便,同时为了符合运算符重载的理念(简便),所以只需要直接使用it->_date这种调用方式就可以。))

!= 与 ==

bool operator!=(const self& it)
{
	return _node!= it._node;	//it是一个迭代器类,而不是一个原生指针
}

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

当节点一致时,迭代器相同;否则就是不相同。

3.list的实现

list主体部分,内部主要实现begin()、end()迭代器,insert、erase等操作。

typedef部分

	template <typename T>
	class list
	{
		typedef list_node<T> Node;

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

内部进行了三次typedef,将节点命名为Node、将迭代器进行typedef的方式去定义。

因此在使用迭代器时,采用 list<T>::iterator it = lt.begin();时,模板类iterator、const_iterator实际上都借助T,完成了模板的实例化。

成员变量

	private:
		Node* _head;
		size_t _size;
	};

_head是哨兵位。_size用来记录有效节点个数。

成员方法

1.迭代器的实现

分为const迭代器与非const迭代器。

const_iterator begin() const
{	
		//return _head->_next;
	return const_iterator(_head->_next);		//支持隐式类型转换(单参数)
}

const_iterator end() const
{
	return const_iterator(_head);		//支持隐式类型转换(单参数)
}

iterator begin()
{
	return iterator(_head->_next);
}

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

在返回时,也可以直接返回原生指针_head。因为iterator的构造函数,是一个单参的构造,支持内部的隐式类型转化。

2.构造函数、拷贝构造、赋值重载

我们提供了全缺省的默认构造

void empty_init()
{
	_head = new Node;	//new + 类型 会自动调用该类型的默认构造函数
	_head->_prev = _head;
	_head->_next = _head;
	_size = 0;
}

list()
{
	empty_init();
}

在empty_init中,类似于进行了一个GetNewNode的操作

其中new + 类型会自动调用该类型的构造方法。

拷贝构造:

由于我们已经实现了迭代器的begin()、end()函数,就可以使用范围for进行容器的遍历。在遍历时,只需要push_back()即可。


		// lt2(lt1)
		list(const list<T>& lt)
		{
			empty_init();

			for (auto& ch : lt)		//实现迭代器之后,就可以使用范围for
			{
				push_back(ch);
			}
		}

先将lt2进行初始化,然后再不断尾插即可。

赋值重载:


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

		//lt2 = lt1
		list<T>& operator=(list<T> tmp)	//tmp借助实参的拷贝构造实现
		{
			swap(tmp);		//tmp在结束时自动调用析构
			return *this;
		}

借助swap()函数与拷贝构造,便可以实现赋值重载。tmp会自己销毁,不需要调用析构函数。

3.析构函数

析构是为了完成资源的清理,可以借助clear完成链表有效数据部分的清理,同时再清除哨兵位就可以。

	~list()
	{
		clear();
		delete _head;
		_head = nullptr;
	}

4.insert

三步骤:1.获取新节点(new操作) 2.记录位置 3.链接节点 

完成之后需要进行++_size。


		//prev newnode cur
		iterator insert(iterator pos, const T& x)	//pos是一个单独的类,而不是原生指针
		{
			Node* cur = pos._node;		//借助三个节点完成
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;

			//prev newnode cur
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;

			++_size;

			return newnode;		//单参
		}

        插入完成之后,返回新节点的迭代器

5.erase函数

完成erase的三步骤:1.记录位置 2.链接节点,删除旧节点 3.--_size;


		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			delete cur;
			prev->_next = next;
			next->_prev = prev;
			--_size;

			return next;
		}

返回删除节点的下一个位置

6.clear函数

用来完成数据的清除,但不会清除哨兵位。

在清除数据时,可以使用erase去删除各个节点。


		void clear()
		{
			iterator it = begin();

			while (it != end())
			{
				it = erase(it);
			}
		}

7.头插、尾删、、、、、、


		void push_back(const T& x)
		{
			insert(end(), x);	//end()指向有效位置的下一个(哨兵位)
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front()
		{
			erase(begin());
		}
		
		void pop_back()
		{
			erase(--end());		//end()是哨兵位,不指向有效数据
		}

需要注意的是,进行尾删时,需要传入--end()。

8.size

	size_t size()
	{
		return _size;
	}

(((

当我们完成list的实现之后,如果想要打印list,就可以在全局中定义一个模板函数。模板参数时Container

在函数内部借助迭代器完成数据的打印。


	template<typename Container>
	void PrintContainer(const Container& con)
	{
		typename Container::const_iterator it = con.begin();	//auto it = con.begin();
		while (it != con.end())
		{
			std::cout << *it << " ";
			++it;
		}
		std::cout << "\n";
	}

)))

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值