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); }