list
list文档
namespace ding
{
//结点类
template<class T>
struct _list_node
{
//构造函数
_list_node(const T& val = T());
T _data;
_list_node<T>* _next;
_list_node<T>* _prev;
};
//迭代器类
template<class T, class Ref, class Ptr>
struct _list_iterator
{
typedef _list_node<T> node;
typedef _list_iterator<T, Ref, Ptr> self;
//构造函数
_list_iterator(node* pnode);
self operator++();
self operator--();
self operator++(int);
self operator--(int);
bool operator==(const self& s) const;
bool operator!=(const self& s) const;
Ref operator*();
Ptr operator->();
//成员变量
node* _pnode;
};
//list类
template<class T>
class list
{
public:
typedef _list_node<T> node;
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
//Member functions
list();
list(const list<T>& lt);
list<T>& operator=(const list<T>& lt);
~list();
//Iterators:
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//Element access:
T& front();
T& back();
const T& front() const;
const T& back() const;
//Modifiers:
void insert(iterator pos, const T& x);
iterator erase(iterator pos);
void push_back(const T& x);
void pop_back();
void push_front(const T& x);
void pop_front();
//Capacity:
size_t size() const;
void resize(size_t n, const T& val = T());
void clear();
bool empty() const;
void swap(list<T>& lt);
private:
node* _head;
};
}
list底层采用了带头双向循环链表的结构实现。
在实现list前,需要定义出一个一个结点出来。直接定义一个结点类,让结点类完成结点的构造即可。
template<class T>
struct _list_node
{
_list_node(const T& val = T())
:_data(val)
,_next(nullptr)
,_prev(nullptr)
{}
T _data;
_list_node<T>* _next;
_list_node<T>* _prev;
};
解决方法:
结合之前string类和vector类的实现得知迭代器要么就是原生指针,要么就是自定义类型对原生指针的一种封装,去模拟指针的行为。比如对结点指针自增就能指向下一个结点
迭代器就是对结点指针进行封装,这里只需要一个结点指针成员变量即可。
_list_iterator(node* node)
{
_node = node;
}
self operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
typedef _list_iterator self
就是迭代器类类型self operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
解引用操作符,是想拿到地址的内容,直接返回当前结点的数据内容即可。
Ref operator*()
{
return _node->_data;
}
注意:
这里的返回值是Ref,在定义迭代器类是时候定义了三个模板参数,T就是指定的类型,Ref则是指定类型的引用类型,也就是T&,Ptr则是指定类型的指针类型,也就是T*。
在list的实现中,
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
迭代器经常需要进行判断两个迭代器是否相等不相等的操作,这里这需要判断两个迭代器中的结点是否相等即可,不需要做其他操作,定义出成const更为合理。
bool operator==(const self& s) const
{
return _node == s._node;
}
bool operator!=(const self& s) const
{
return _node != s._node;
}
这个操作符对于迭代器类型并不是很常用,但是为了模拟指针的行为,指针有->操作符,迭代器就模拟实现了。
运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。也就是这里返回值必须是Ptr 指定类型T*类型。
Ptr operator->()
{
return &(_node->_data);
}
使用->场景:
当list中存放的是自定义类型,
class Date
{
public:
Date(int year)
{
_year = year;
}
int _year = 0;
};
int main()
{
std::list<Date>lt;
lt.push_back(2023);
lt.push_back(2024);
auto it = lt.begin();
cout << it->_year << endl;
return 0;
}
可以使用->访问类的成员变量。
list()
{
_head = new node;
_head->_prev = _head;
_head->_next = _head;
}
这里的node是经过typedef得。typedef _list_node
对结点类起的别名
构造一个链表即可,这里得空链表是需要一个头节点得。并且让自己指向自己。
申请一个新的头结点,再将源容器中的数据依次尾插到新容器中即可
list(const list<T>& lt)
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
for (auto val : lt)
{
push_back(val);
}
}
list<T>& operator=(const list<T>& lt)
{
if (this != <)
{
clear();
for (const auto e : lt)
{
push_back(e);
}
}
return *this;
}
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
_head = new node;
_head->_prev = _head;
_head->_next = _head;
while (first != last)
{
push_back(*first);
++first;
}
}
先清空容器在释放头节点即可
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it != end())
{
erase(it++);
}
}
begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器
底层是双向循环链表实现的,所以头结点的下一个就是gebin,头节点就是end。
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head->_prev);
}
const_iterator begin() const
{
return iterator(_head->_next);
}
const_iterator end() const
{
return iterator(_head->_prev);
}
在pos位置前面插入一个结点
void insert(iterator pos, const T& x)
{
node* newnode = new node(x);//创建新结点
node* cur = pos._node;//pos位置的结点指针
node* prev = cur->_prev;//pos前一个
//连接关系
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
//尾插
void push_back(const T& x)
{
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
删除pos位置的结点
iterator erase(iterator pos)
{
node* cur = pos._node;//当前结点指针
node* prev = cur->_prev;//pos位置前一个结点
node* next = cur->_next;//pops位置后一个结点
//连接关系
prev->_next = next;
next->_prev = prev;
//释放删除的结点
delete cur;
return iterator(next);//防止迭代器失效,返回pos位置下一个迭代器
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
size_t size() const
{
size_t size = 0;
auto it = begin();
while (it != end())
{
size++;
it++;
}
return size;
}
void clear()
{
auto it = begin();
while (it != end())
{
pop_back();
}
}
bool empty() const
{
return _head->_next;
}
void resize(size_t n, const T& val = T())
{
size_t sz = size();
if (sz < n)//扩容
{
for (; sz < n; ++sz)
{
push_back(val);
}
}
else//不扩容
{
if (sz > n)//缩容
{
int len = sz - n;
cout << len << endl;
while (len--)
{
pop_back();
}
}
}
}
交换两个容器的头指针即可
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
}