stl_list

list的迭代器失效

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
auto pos = find(lt.begin(), lt.end(), 3);
if (pos != lt.end())
{
    lt.insert(pos, 300);
    // pos不会失效
    cout << *pos << " ";
    *pos = 3000;
}
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
    cout << *it << " ";
    ++it;
}
---------------------------------
auto pos = find(lt.begin(), lt.end(), 3);
if (pos != lt.end())
{
    lt.erase(pos);
    // pos会失效,因为这个位置的节点已经被删除了,再次访问会报错
    cout << *pos;
}

list的实现

简单的实现

// 节点
template <class T>
struct list_node
{
	list_node* _next;
	list_node* _prev;
	T _data;
	list_node(const T& x = T())
		:_next(nullptr)
		,_prev(nullptr)
		,_data(x)
	{}
};

// 迭代器
template <class T>	
struct __list_iterator
{
	typedef list_node<T> node;
	typedef __list_iterator<T> self;
	node* _node;
	
	// 构造函数
	__list_iterator(node* x) 
		:_node(x){}
	T& 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;
	}
};
// 链表
template<class T>
class list
{
public:
	typedef list_node<T> node;
	typedef __list_iterator<T> iterator;
public:
    // 哨兵位节点
	void empty_init()
    {
        _head = new node;
        _head->_next = _head;
        _head->_prev = _head;
    }
	list()	{ empty_init(); }
	iterator begin() { return iterator(_head->_next); }
	iterator end() { return iterator(_head); }	 // end是_head;
	void push_back(const T& x)
	{
		node* tail = _head->_prev;	// 找到尾节点
		node* newNode = new node(x);	// new一个新节点
		// 建立链接
		tail->_next = newNode;
		newNode->_prev = tail;
		newNode->_next = _head;
		_head->_prev = newNode;
	}
private:
	node* _head;	// 哨兵位
};

const_iterator

但是如果想实现一个const_iterator迭代器,可以再写一个__list_const_iterator结构体,但是重复的代码太多
所以将迭代器参数改成

typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
// __list_iterator部分修改为
template <class T, class Ref, class Ptr>	
struct __list_iterator
{
	typedef list_node<T> node;
	typedef __list_iterator<T, Ref, Ptr> self;
	node* _node;
    
   	Ref& operator*()
    {
        // 重载解引用运算符
        return _node->_data;
    }
    // ...
}

有人会添加一个typedef const __list_iterator<T> const_iterator来避免上面这种传三个模版参数的写法,
这是错误的,因为我们需要的是const T*而不是T* const

第三个参数的作用

__list_iterator结构体中,第三个参数Ptr的作用是重载下面这个函数

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

例如

struct MyStruct
{
	int _x; int _y;
	MyStruct(int x = 0, int y = 0) : _x(x), _y(y) {}
};
void test_list2()
{
	// 测试->运算符重载
	list<MyStruct> lt;
	lt.push_back(MyStruct(1, 1));
	lt.push_back(MyStruct(2, 2));
	lt.push_back(MyStruct(3, 3));
	lt.push_back(MyStruct(4, 4));
	auto it = lt.begin();
	while (it != lt.end())
	{
		cout << it->_x << ',' << it->_y << endl;
       	// 同cout << it.operator->()->_x << ',' << it.operator->()->_y << endl;
		++it;
	}
}

由上面可见,c++为了增强可读性.对->进行了优化,在使用的时候不需要写成it->->就可以访问到容器里面的值

empty_init

上面的empty_init并没有用const修饰,但是不用担心const 对象调不动这个函数,因为我们一般认为const对象在定义时是没有常性的

const int a = 30;		// 如果在定义时const就有常性,我们就无法对其赋值!
a = 20;	// err,定义之后才具有常性

insert,erase

void 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;
}
void erase(iterator pos)
{
    assert(pos != end());
	// erase会导致迭代器失效,因为pos位置的节点已经被删除了
	node* prev = pos._node->_prev;
	node* next = pos._node->_next;
	prev->_next = next;
	next->_prev = prev;
	delete pos._node;	// 没有中括号
}

其他的函数

~list()
{
	clear();
	delete _head;
	_head = nullptr;
}
void clear()
{
	list<T>::iterator it = begin();
	while (it != end())
	{
		// it = erase(it);
		erase(it++);	// 也可以这样写,因为是后置++,删除的是那个位置的拷贝,it正常到下一个节点
	}
}
// 拷贝构造 和 赋值的现代写法
list(const list<T>& lt)
{
	empty_init();
	list<T> tmp(lt.begin(), lt.end());
	swap(tmp);
}
list<T>& operator=(list<T> tmp)
{
	swap(tmp);
	return *this;
}
void swap(list<T>& lt)
{ ::swap(_head, lt._head); }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值