目录
一、list的使用
1.1list的构造
1.2list的iterator
编辑
1.3 list的capacity
1.4 list的element access
编辑
1.5list的mdifiers
编辑
二、list的迭代器失效问题
三、list的模拟实现
3.1定义一个节点类
3.2用节点去封装迭代器
编译器对->的优化问题
编辑
Ref和Ptr
3.3list基本功能的实现
四、vector和list的比较
4.1vector
4.2list
一、list的使用
1.1list的构造
1.2list的iterator
1.3 list的capacity
1.4 list的element access
1.5list的mdifiers
二、list的迭代器失效问题
list的底层是双向带头循环链表,在实现插入功能的时候是一边扩容一边插入,且都是在原空间上进行操作的,因此list的插入不存在迭代器失效问题;但list的删除就会使该节点位置的迭代器失效,其他位置的迭代器不受影响,如果要解决这个问题,仍然是用迭代器去接收返回值。
三、list的模拟实现
3.1定义一个节点类
//节点
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _val;
list_node(const T& x = T())
:_prev(nullptr)
,_next(nullptr)
,_val(x)
{}
};
节点类就相当于c语言中的BuyNewNode()
3.2用节点去封装迭代器
因为list的底层结构是不连续的,指向节点的指针++不一定是list下一个位置的节点,因此不能使用原生指针来作为list的迭代器。这里的解决办法是用节点指针(原生指针)去封装迭代器,在这个迭代器类的内部去模拟实现原生指针的功能,即用运算符重载实现适配于list的++ -- != == * ->等操作
//用节点(自定义类型)封装的迭代器
//typedef _list_iterator<T,T&,T*> iterator;
//typedef _list_iterator<T, const T&, const T*> const_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;
_list_iterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
_list_iterator temp(*this);
_node = _node->_next;
return temp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
_list_iterator temp(*this);
_node = _node->_prev;
return temp;
}
bool operator!=(const self& lt) const
{
return _node != lt._node;
}
bool operator==(const self& lt) const
{
return _node == lt._node;
}
};
编译器对->的优化问题
创建一个存储结构体的list对象,结构体对成员变量的访问可以通过 变量名.成员变量 或者 结构体指针->成员变量 来实现。我们在重载*和->的时候可以发现,对it进行*解引用操作找到的是一个结构体,该结构体使用.操作符去访问成员变量,这是合理的;但是对it进行->操作时找到的是一个结构体指针,结构体指针要去访问成员变量,应该再次使用->,it->_a1代码应该是写成(it->)->_a1的,但是编译器对其进行了优化,直接用一个->就可以实现迭代器对结构体成员变量的访问
Ref和Ptr
在实现const_iterator时,不能直接typedef const _list_iterator<T, Ref ,Ptr> const_self,因为const的作用是不能改变迭代器所指向的内容,迭代器本身是可以改变的。所以要实现const_list_iterator 只需要在_list_iterator这个类内部对*和->的返回值进行限制。那么*和->的返回值就不只有T& 和T*,还有const T& 和const T*,将返回值重新定义为新的模板参数Ref和Ptr
3.3list基本功能的实现
//双向带头循环链表
template<class 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;
iterator begin()
{
//return _head->_next;
return iterator(_head->_next);
}
iterator end()
{
//return _head;
return iterator(_head);
}
const_iterator begin() const
{
//return _head->_next;
return const_iterator(_head->_next);
}
const_iterator end() const
{
//return _head;
return const_iterator(_head);
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
//拷贝构造
list(const list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//赋值运算符重载
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
it++;
}
_size = 0;
}
void push_back(const T& x = T())
{
/*Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;*/
insert(end(), x);
}
void push_front(const T& x = T())
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
//在pos位置之前插入数据
iterator insert(iterator pos, const T& x=T())
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
newnode->_next = cur;
cur->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
_size++;
return newnode;
}
//删除pos位置的数据
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* next = cur->_next;
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
_size--;
return next;
}
size_t size()
{
return _size;
}
private:
Node* _head;
size_t _size;
};
}
四、vector和list的比较
4.1vector
vector的底层是一块连续的空间,支持用[下标]随机访问元素,时间复杂度为O(1);插入和删除的时候需要搬移元素(插入有时候还需要扩容),时间复杂度为O(N);对空间的利用率高,不容易造成内存碎片,缓存利用率高;迭代器使用原生指针,插入和删除都会导致迭代器的失效问题;适用于需要高效存储,支持随机访问,不关心插入和删除的场景
4.2list
list的底层是一段不连续的空间,不支持[下标]随机访问,访问元素需要遍历,时间复杂度为O(N);插入和删除的时候直接对该位置进行操作,不需要搬移元素,时间复杂度为O(1);对空间的利用率低,容易造成内存碎片,缓存利用率低;迭代器是对原生指针(节点指针)的封装,插入时不存在迭代器失效问题,但删除时会导致迭代器的失效;适用于需要大量的插入和删除,但不关心随机访问的场景