1.list的使用
1.1list的构造
|
构造函数( (constructor))
|
接口说明
|
|
list (size_type n, const value_type& val =
value_type())
|
构造的list中包含n个值为val的
元素
|
|
list()
|
构造空的list
|
|
list (const list& x)
|
拷贝构造函数
|
|
list (InputIterator first, InputIterator last)
|
用[first, last)区间中的元素构造
list
|
1.2 list iterator的使用
|
函数声明
|
接口说明
|
|
begin + end
|
返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
|
|
rbegin
+ rend
|
返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置
|
1.3 list capacity
|
函数声明
|
接口说明
|
|
empty
|
检测list是否为空,是返回true,否则返回false
|
|
size
|
返回list中有效节点的个数
|
1.4 list element access
| 函数声明 | 接口说明 |
|
front
|
返回list的第一个节点中值的引用
|
|
back
|
返回list的最后一个节点中值的引用
|
1.5 list modifiers
|
函数声明
| 接口说明 |
|
push_front
|
在list首元素前插入值为val的元素
|
|
pop_front
|
删除list中第一个元素
|
|
push_back
|
在list尾部插入值为val的元素
|
|
pop_back
|
删除list中最后一个元素
|
|
insert
|
在list position 位置中插入值为val的元素
|
|
erase
|
删除list position位置的元素
|
|
swap
|
交换两个list中的元素
|
|
clear
|
清空list中的有效元素
|
emplace_back和emplace_front与push_back和push_front很像,只有在比如说push_back({1,2}),里面传的是自定义类型类的隐式类型转换时,不能使用emplace_back({1,2}),emplace_back(1,2)直接写这种,在这种使用上emplace_back更有效率。
assign是clear与构造n个val的结合,
1.6 list operations
| 函数声明 | 接口说明 |
| remove | 删除链表的指定元素(指定元素没有也不会报错) |
| unique | 移除连续的重复元素 |
| merge | 合并已经排序好的两个链表 |
| sort | 排序:list完成的是双向迭代器(用到的是归并排序),但算法库的sort需要用随机迭代器 |
为什么算法里有排序还要再类里实现sort那
迭代器按照性质可以分为单向迭代器(++)、双向迭代器(++/--)、随机迭代器(++/--/+/-)
算法用RandomAccessIterator需要用随机迭代器(只能用迭代器是随机迭代器的容器string、vector、deque)
算法用BidrectionalIterator需要用双向迭代器(双向迭代器的容器list、map、set),也可以用随机迭代器
算法用InputIterator需要用单项迭代器(单项迭代器的容器forword_list、unorder_map、unorder_set),双向迭代器和随机迭代器也可以使用
用sort排序,默认是排升序,如果想要排降序,需要用到仿函数greater<int> gt,一般用匿名对象,如lt1.sort(greater<int>());
splice粘贴:把链表的一部分粘到另一个链表的某一个位置之前
2. list的模拟
首先在进行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)
{
}
};
为什么用struct 来申请这个类,因为这样下面申请的链表list的类才能够访问其中的成员变量,当然这不是绝对的,也可以把成员变量放在class定义的类的public区。
在c语言中创建结点实现链表的时候,并不需要构造函数,但这里需要。这里为后面进行new 操作提供了前提。
template<class T>
class list
{
typedef list_node<T> Node;
public:
list()
{
_node = new Node;
_node->_next = _node;
_node->_prev = _node;
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* prev = _node->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = _node;
_node->_prev = newnode;
}
private:
Node* _node;
};
注意:typedef放在public区和private区是不一样的,想要在外部访问就typedef在public区,不然就放在private区。
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
T& operator*()
{
return _node->_data;
}
list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
//const需要存在,因为list.begin()可能返回的是右值
bool operator==(const list_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_iterator<T>& it)
{
return _node != it._node;
}
};
当使用迭代器的时候,就要对迭代器进行解引用++、--等相关操作,但是node*解引用并不能得到数据,所以简单的将node* typedef成iterator是不可以的。所以要重新封装迭代器,由于node*不能满足迭代器的需求,但每个结点之间的关联还是需要node* 的,所以重新封装node*,重载解引用、++、--等。
当处理的是自定义类型的数据时, 就涉及到应用->的操作,
struct A
{
A(int a1 = 1, int a2 = 1)
:_a1(a1)
, _a2(a2)
{}
int _a1;
int _a2;
};
int main()
{
zyf::list<A> lt2;
lt2.push_back({ 1,1 });
lt2.push_back({ 2,2 });
lt2.push_back({ 3,3 });
lt2.push_back({ 4,4 });
zyf::list<A>::iterator it = lt2.begin();
while (it != lt2.end())
{
cout << it->_a1 << ";" << it->_a2 << endl;
++it;
}
return 0;
}
而对于->的重载是
T* operator->()
{
return &_node->_data;
}
其实这里看起来并不对,为什么可以node*->_a1直接取,事实上本应该有两个->,但是编译器为了可读性,做了特殊处理,省略了一个箭头。it->指向的是A*,再->指向的就是_a1,或者_a2.
在遍历的过程中,对iterator指向的内容不想修改,就需要const T&和const T*
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const list_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_iterator<T>& it)
{
return _node != it._node;
}
};
template<class T>
struct list_const_iterator
{
typedef list_node<T> Node;
Node* _node;
list_const_iterator(Node* node)
:_node(node)
{
}
const T& operator*()
{
return _node->_data;
}
const T* operator->()
{
return &_node->_data;
}
list_const_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const list_const_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_const_iterator<T>& it)
{
return _node != it._node;
}
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T> iterator;
typedef list_const_iterator<T> const_iterator;
这里用两段来对iterator和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->_data;
}
Ptr operator->()
{
return &_node->_data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const self& it)
{
return _node == it._node;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
};
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;
1013

被折叠的 条评论
为什么被折叠?



