一、list的简单介绍
学习过数据结构中的带头双向循环链表的铁子,对于理解list应该不难,list就是一个带头双向循环的链表。

list和vector、string的使用是差不多的,都可以使用迭代器,尾插,尾删这些,但是list不支持[],也就是随机访问,这也好理解,list的空间并不是连续的。
二、list模拟实现的三个类
1、节点类
list是由一个一个节点连接起来的,一个节点包括一个数据段,一个前驱指针和一个后驱指针。(注意链表中叫节点,结点都是可以的,随你喜欢)

template <class T>
struct list_node
{
T _data; // 三个成员变量
list_node<T>* _next;
list_node<T>* _prev;
list_node(const T& x = T())
// 节点的构造,直接给值就行,这里给的是缺省函数,匿名对象,另外两个指针给空就行
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{
}
};
2、迭代器类
我们知道迭代器分为普通迭代器和const迭代器,我们在模拟实现string和vector的使用,为什么没有实现迭代器类呢?
这是因为string和vector的空间是连续的,它们的迭代器是原生指针,本身就可以进行++,–,解引用操作。
但是list的空间并不是连续的,要访问下一个节点,需要使用node = node -> _next,其他操作类似,所以list的迭代器没法像vector迭代器那样直接使用,所以需要我们自己封装一下迭代器,这里就会使用到各种赋值运算符重载,可见赋值运算符重载的地位举足轻重。
下面的迭代器类是普通迭代器的:
template<class T>
struct __list_iterator
{
typedef list_node<T> Node;
typedef __list_iterator<T> self; // 这里简化写法,我们使用typedef
Node* _node;
__list_iterator(Node* node) // 迭代器只需要一个节点指针来构造即可
:_node(node)
{
}
self& operator++();
self& operator--();
self operator++(int);
self& operator+(size_t x);
self operator--(int);
T& operator*();
T* operator->();
bool operator!=(const self& s);
bool operator==(const self& s);
};
3、链表类
template <class T>
class list
{
typedef list_node<T> Node;
public:
typedef __list_iterator<T> iterator;
// 迭代器相关函数
iterator begin();
iterator end();
// 默认成员函数
void empty_init();
list()
{
empty_init();
}
list(list<T>& lt) // 拷贝构造函数
~list(); // 析构函数会将整个链表清理干净,包括头节点
// 其他函数
void swap(list<T>& lt);
list<T>& operator=(list<T>& lt);
void push_back(const T& x); // 尾插
void pop_back(); // 尾删
void push_front(const T& x); // 头插
void pop_front(); // 头删
iterator insert(iterator pos, const T& x);
iterator erase(iterator pos); // 因为执行了delete删除该节点,所以当前迭代器失效了,需要有返回值
size_t size();
void clear(); // 除了头节点全部释放
private:
Node* _head; // 哨兵位的头节点
size_t _size; // 记录节点的个数
};
三、迭代器类的模拟实现
1、普通迭代器类
1.1、operator++()和operator++(int)
为了区分前置++和后置++,所以在参数的位置加上一个int来区分,operator++()是前置++,而operator++(int)是后置++,前置++是先++后使用,而后置++是先试用后++。
operator++():前置++
self& operator++()
{
_node = _node->_next;
return *this;
}
operator++(int):后置++
self operator++(int)
{
self tmp(*this); // 先使用临时变量拷贝下来
_node = _node->_next; // 再到下一个节点
return tmp; // 最后再返回该临时变量
}
注意这里的返回值不能使用&返回,因为tmp是一个临时变量,函数结束就释放掉了。
1.2、operator–()和operator–(int)
后置–和前置–其实和前置的是一样的,只不过使用_prev而已。
self& operator--() // 前置--
{
_node = _node->_prev;
return *this;
}
self operator--(int) // 后置--
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
1.3、operator*()
*是解引用的符号,也就是访问节点的数据段。
T& operator*()
{
return _node->_data;
}
1.4、operator->()
->是得到节点数据段位置的地址。
Ptr operator->()
{
return &_node->_data;
}
这里重载->是有一点讲究的。举个例子,日期类的实现来说:
class Date
{
public:
int _year;
int _month;
int _day;
Date(int year = 0, int month = 0, int day = 0)
:_year(year)
,_month(month)
,_day(day)
{
}
};
int main(


2861

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



