list 是一种双向链表。list 的设计更加复杂一点,好处是每次插入或删除一个元素,就配置或释放一个元素,list 对于空间的运用有绝对的精准,一点也不浪费。而且对于任何位置的元素插入或删除,list 永远是常数空间。
list 源码分成了两个部分,一个部分是 list 结构,另一部分是 list 节点的结构。
为什么 list 节点分为了两个部分,而不是在一个结构体里面呢? 也就是说为什么指针变量和数据变量分开定义呢?这里是为了给迭代器做铺垫,因为迭代器遍历的时候不需要数据成员的,只需要前后指针就可以遍历该 list。
1. list node结构
__list_node
用来实现节点,数据结构中就储存前后指针和属性。
template <class T> struct __list_node {
// 前后指针
typedef void* void_pointer;
void_pointer next;
void_pointer prev;
// 属性
T data;
};
基本类型:
template<class T, class Ref, class Ptr> struct __list_iterator {
typedef __list_iterator<T, T&, T*> iterator; // 迭代器
typedef __list_iterator<T, const T&, const T*> const_iterator;
typedef __list_iterator<T, Ref, Ptr> self;
// 迭代器是bidirectional_iterator_tag类型
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
...
};
构造函数:
template<class T, class Ref, class Ptr> struct __list_iterator {
...
// 定义节点指针
typedef __list_node<T>* link_type;
link_type node;
// 构造函数
__list_iterator(link_type x) : node(x) {}
__list_iterator() {}
__list_iterator(const iterator& x) : node(x.node) {}
...
};
重载:
template<class T, class Ref, class Ptr> struct __list_iterator {
...
// 重载
bool operator==(const self& x) const { return node == x.node; }
bool operator!=(const self& x) const { return node != x.node; }
...
// ++和--是直接操作的指针指向next还是prev, 因为list是一个双向链表
self& operator++() {
node = (link_type)((*node).next);
return *this;
}
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
self& operator--() {
node = (link_type)((*node).prev);
return *this;
}
self operator--(int) {
self tmp = *this;
--*this;
return tmp;
}
};
2. list 结构
list 迭代器是 bidirectional_iterator_tag
类型,并不是一个普通指针。list在定义 node 节点时, 定义的不是一个指针。
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node; // 节点 就是前面分析过的
typedef simple_alloc<list_node, Alloc> list_node_allocator; // 空间配置器
public:
// 定义嵌套类型
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef list_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
// 定义一个节点, 这里节点并不是一个指针.
link_type node;
public:
// 定义迭代器
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
...
};
3. 成员函数
成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的双向迭代器。 |
end() | 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。 |
rbegin() | 返回指向最后一个元素的反向双向迭代器。 |
rend() | 返回指向第一个元素所在位置前一个位置的反向双向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。 |
size() | 返回当前容器实际包含的元素个数。 |
max_size() | 返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。 |
front() | 返回第一个元素的引用。 |
back() | 返回最后一个元素的引用。 |
assign() | 用新元素替换容器中原有内容。 |
emplace_front() | 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。 |
push_front() | 在容器头部插入一个元素。 |
pop_front() | 删除容器头部的一个元素。 |
emplace_back() | 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。 |
push_back() | 在容器尾部插入一个元素。 |
pop_back() | 删除容器尾部的一个元素。 |
emplace() | 在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。 |
insert() | 在容器中的指定位置插入元素。 |
erase() | 删除容器中一个或某区域内的元素。 |
swap() | 交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。 |
resize() | 调整容器的大小。 |
clear() | 删除容器存储的所有元素。 |
splice() | 将一个 list 容器中的元素插入到另一个容器的指定位置。 |
remove(val) | 删除容器中所有等于 val 的元素。 |
remove_if() | 删除容器中满足条件的元素。 |
unique() | 删除容器中相邻的重复元素,只保留一个。 |
merge() | 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。 |
sort() | 通过更改容器中元素的位置,将它们进行排序。 |
reverse() | 反转容器中元素的顺序。 |
4. 析构和构造
每个构造函数都会创造一个空的 node 节点,为了保证我们在执行任何操作都不会修改迭代器。
list 默认使用 alloc 作为空间配置器,并根据这个另外定义了一个 list_node_allocator,目的是更加方便以节点大小来配置单元。
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node; // 节点
typedef simple_alloc<list_node, Alloc> list_node_allocator; // 空间配置器
}
其中,list_node_allocator(n)
表示配置 n 个节点空间。以下四个函数,分别用来配置,释放,构造,销毁一个节点。
class list {
protected:
// 配置一个节点并返回
link_type get_node() { return list_node_allocator::allocate(); }
// 释放一个节点
void put_node(link_type p) { list_node_allocator::deallocate(p); }
// 产生(配置并构造)一个节点带有元素初始值
link_type create_node(const T& x) {
link_type p = get_node();
__STL_TRY {
construct(&p->data, x);
}
__STL_UNWIND(put_node(p));
return p;
}
//销毁(析构并释放)一个节点
void destroy_node(link_type p) {
destroy(&p->data);
put_node(p);
}
// 对节点初始化
void empty_initialize() {
node = get_node();
node->next = node;
node->prev = node;
}
};
5. 基本属性获取
template <class T, class Alloc = alloc>
class list {
...
public:
iterator begin() { return (link_type)((*node).next); } // 返回指向头的指针
const_iterator begin() const { return (link_type)((*node).next); }
iterator end() { return node; } // 返回最后一个元素的后一个的地址
const_iterator end() const { return node; }
// 这里是为旋转做准备, rbegin返回最后一个地址, rend返回第一个地址. 我们放在配接器里面分析
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// 判断是否为空链表, 这是判断只有一个空node来表示链表为空.
bool empty() const { return node->next == node; }
// 因为这个链表, 地址并不连续, 所以要自己迭代计算链表的长度.
size_type size() const {
size_type result = 0;
distance(begin(), end(), result);
return result;
}
size_type max_size() const { return size_type(-1); }
// 返回第一个元素的值
reference front() { return *begin(); }
const_reference front() const { return *begin(); }
// 返回最后一个元素的值
reference back() { return *(--end()); }
const_reference back() const { return *(--end()); }
// 交换
void swap(list<T, Alloc>& x) { __STD::swap(node, x.node); }
...
};
template <class T, class Alloc>
inline void swap(list<T, Alloc>& x, list<T, Alloc>& y) {
x.swap(y);
}
6. 插入和删除操作
在 list 中,push 操作都调用 insert 函数, pop 操作都调用 erase 函数。
template <class T, class Alloc = alloc>
class list {
...
// 直接在头部或尾部插入
void push_front(const T& x) { insert(begin(), x); }
void push_back(const T& x) { insert(end(), x); }
// 直接在头部或尾部删除
void pop_front() { erase(begin()); }
void pop_back() {
iterator tmp = end();
erase(--tmp);
}
...
};
上面的两个插入函数内部调用的 insert 函数。
class list {
...
public:
// 最基本的insert操作, 之插入一个元素
iterator insert(iterator position, const T& x) {
// 将元素插入指定位置的前一个地址
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}
};
删除元素的操作大都是由 erase 函数来实现的, 其他的所有函数都是直接或间接调用 erase。list 是链表, 所以链表怎么实现删除, list 就在怎么操作:很简单,先保留前驱和后继节点, 再调整指针位置即可。由于它是双向环状链表,只要把边界条件处理好,那么在头部或者尾部插入元素操作几乎是一样的,同样的道理,在头部或者尾部删除元素也是一样的。
template <class T, class Alloc = alloc>
class list {
...
iterator erase(iterator first, iterator last);
void clear();
// 参数是一个迭代器 修改该元素的前后指针指向再单独释放节点就行了
iterator erase(iterator position) {
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destroy_node(position.node);
return iterator(next_node);
}
...
};
标签:node,容器,typedef,const,iterator,list,type From: https://www.cnblogs.com/love-9/p/18149168