list

目录

list和vector的比较

list的模拟实现

list类的结点定义,成员变量

构造函数和析构函数

clear 

迭代器的实现

begin() 和 end()

insert 和 erase

push_back/push_front/pop_back/pop_front

拷贝构造

赋值运算符重载

swap

整体代码


list和vector的比较

vector的优点:支持下标随机访问。间接的就可以支持排序、二分查找、堆算法等等
缺点:
1、头部和中部的插入和删除效率低,因为需要挪动数据。
2、插入数据时,当空间不够需要增容。开辟新的空间,拷贝数据,释放旧空间,代价很大

list的优点:
1、头部和中部插入删除不需要挪动数据,效率高
2、插入数据时,不需要增容,新增一个结点即可
缺点:不支持随机访问。

迭代器失效:
对于vector进行插入和删除时,(当插入操作时,有可能空间不够,需要增容,那么当前迭代器指向的空间就被释放了)迭代器会失效,需要对迭代重新赋值
对于list进行插入,迭代器不会失效;删除时,迭代器也会失效

底层空间:
vector底层空间是一块连续的空间,不容易造成内存碎片,空间利用率高。
list底层空间是一个一个的动态开辟的结点,容易造成内存碎片,空间利用率低。

总的来说:list和vector相辅相成

list的模拟实现

list类的结点定义,成员变量

template<class T>
struct _list_node
{
	_list_node<T>* _prev;
	_list_node<T>* _next;
	T _data;
	_list_node(const T& data = T())  // T()是匿名对象做缺省值
		:_prev(nullptr)
		,_next(nullptr)
		,_data(data)
	{}
};

template<class T>
class list
{
	typedef _list_node<T> Node;
private:
	Node* _head;
};

构造函数和析构函数

list()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
}
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

clear 

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		erase(it);
		it++;
	}
}

迭代器的实现

template<class T, class Ref, class Ptr>    // 当加入const的迭代器,需要增加两个参数,Ref是T类型的引用,Ptr是T类型的指针
struct _list_iterator
{
	typedef _list_node<T> Node;
	typedef _list_iterator<T, Ref, Ptr> Self; 
	Node* _node;
	_list_iterator(Node* node)  // 为了将Node*类型转换为迭代器类型
		:_node(node)
	{}

	Ref operator*()  // 当使用普通迭代器时,*it返回一个普通的T类型
	{				// 但是需要使用const迭代器,就需要返回一个const T
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}
	Self& operator++() // 前置++
	{
		_node = _node->_next;
		return *this;
	}

	Self operator++(int)  // 后置++
	{
		Self temp(*this);
		_node = _node->_next; // 也可以++(*this)
		return temp;
	}

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	Self operator--(int)
	{
		Self temp(*this);
		//_node = _node->_prev;
		--(*this);
		return temp;
	}
	bool operator!=(const Self& it)
	{
		return _node != it._node;
	} 
	bool operator==(const Self& it)
	{
		return _node == it._node;
	}
};

list的迭代器封装,不像vector的迭代器,vector的迭代器是一个原生指针。
因为vector是一块连续的空间,通过对指针++,就可以访问到下一个结点
而list是一个个的独立的结点,通过在每个结点中假如prev和next指针将独立的结点连接起来
++操作只能通过node->next;--操作只能通过node->prev
而对迭代器 it 解引用*,也需要通过node->data
所以需要封装迭代器,并且对++、* 运算符重载

begin() 和 end()

typedef _list_node<T> Node; 
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

iterator begin()
{
	return iterator(_head->_next);  // _head->_next是Node*类型,强转为迭代器类型,需要调用封装迭代器类中的构造函数
}
iterator end()
{
	return iterator(_head);  // list是带头的双向循环链表,尾就算最后一个结点的next,也就是head
}

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

Node* cur;         iterator it;
它们在物理内存中都存的是这个结点地址
但是它们的类型不一样,意义也就不一样
比如*cur是一个指针的解引用,取到的值是结点, 而*it,是调用迭代器的operator*,返回的值是结点存的data

insert 和 erase

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

iterator erase(iterator pos)
{
	assert(pos != end());
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;
	prev->_next = next;
	next->_prev = prev;
	delete cur;
	return iterator(next);
}

push_back/push_front/pop_back/pop_front

void push_back(const T& x)
{
	insert(end(), x);
}
void push_front(const T& x)
{
	insert(begin(), x);
}
void pop_back()
{
	erase(--end());  // iterator(_head->_prev)
}
void pop_front()
{
	erase(begin());
}

拷贝构造

list(const list<T>& lt)
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	for (auto e : lt)
	{
		push_back(e);
	}
}

拷贝构造不能像赋值运算符一样,有现代写法,因为现代写法需要形参是传值接受的,那么就需要一个临时对象,这个临时对象是调用拷贝构造创建的,那么就会造成递归调用,爆栈。 

赋值运算符重载

// 传统写法
list<T>& operator=(const list<T>& lt)
{
	if (this != &lt)
	{
		clear();
		for (const auto& e : lt)
		{
			push_back(e);
		}
	}
	return *this;
}

//现代写法
list<T>& operator=(list<T> lt)
{
	swap(lt);
	return *this;
}

这里的赋值运算符重载的现代写法,函数的形参是传值,不是传引用的。那么在调用这个函数的时候,会在函数的栈帧上调用拷贝构造函数,创建一个临时对象。这个临时对象的内容和实参是一样的,然后交换这个临时对象和this指针的内容,出了函数作用域之后,这个临时对象会自动调用析构函数,清理不要的旧资源。 

swap

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

这里的swap是调用全局函数,也可以直接 :: 
假如没有加,那么就会根据就近原则,直接这个swap函数了。

整体代码

#pragma once
namespace yair
{
	template<class T>
	struct _list_node
	{
		_list_node<T>* _prev;
		_list_node<T>* _next;
		T _data;
		_list_node(const T& data = T())  // T()是匿名对象做缺省值
			:_prev(nullptr)
			, _next(nullptr)
			, _data(data)
		{}
	};
	template<class T, class Ref, class Ptr>    // 当加入const的迭代器,需要增加两个参数,Ref是T类型的引用,Ptr是T类型的指针
	struct _list_iterator
	{
		typedef _list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> Self;
		Node* _node;
		_list_iterator(Node* node)  // 为了将Node*类型转换为迭代器类型
			:_node(node)
		{}

		Ref operator*()  // 当使用普通迭代器时,*it返回一个普通的T类型
		{				// 但是需要使用const迭代器,就需要返回一个const T
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}
		Self& operator++() // 前置++
		{
			_node = _node->_next;
			return *this;
		}

		Self operator++(int)  // 后置++
		{
			Self temp(*this);
			_node = _node->_next; // 也可以++(*this)
			return temp;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		Self operator--(int)
		{
			Self temp(*this);
			//_node = _node->_prev;
			--(*this);
			return temp;
		}
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};

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

		iterator begin()
		{
			return iterator(_head->_next);  // _head->_next是Node*类型,强转为迭代器类型,需要调用封装迭代器类中的构造函数
		}
		iterator end()
		{
			return iterator(_head);  // list是带头的双向循环链表,尾就算最后一个结点的next,也就是head
		}

		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it);
				it++;
			}
		}
		iterator insert(iterator pos, const T& x)
		{
			Node* newnode = new Node(x);
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_back()
		{
			erase(--end());  // iterator(_head->_prev)
		}
		void pop_front()
		{
			erase(begin());
		}
		list(const list<T>& lt)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			for (auto e : lt)
			{
				push_back(e);
			}
		}
		 传统写法
		//list<T>& operator=(const list<T>& lt)
		//{
		//	if (this != &lt)
		//	{
		//		clear();
		//		for (const auto& e : lt)
		//		{
		//			push_back(e);
		//		}
		//	}
		//	return *this;
		//}

		//现代写法
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
		}
	private:
		Node* _head;
	};

	void test1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << ' ';
			++it;
		}

	}
	void test2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		lt.push_front(0);
		lt.push_front(-1);
		lt.pop_back();
		lt.pop_front();
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << ' ';
			++it;
		}

	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值