提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
list结构分析
在源码中可以看到list是由一个带头双向循环列表构成的,而要实现这个带头双向循环列表,首先还需要创建一个节点的类listnode,用来存储数据一次上一个节点和下一个节点的指针。而list类就存储节点的头指针。
一、listnode类的创建
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)
{
}
};
节点类包括三个成员变量
data:存储数据
next:存储下一个节点的指针
prev:存储上一个节点的指针
构造函数的列表初始化把next和prev置为空指针。
二、成员函数的实现
1.构造函数
void empty_init() {
_head = new node;
_head->next = _head;
_head->prev = _head;
}
list()
{
empty_init();
}
typedef list_node<T> node;
将listnode重命名位node,先开辟一个node节点当作头指针,然后让next和prev都指向head头节点,这样一个带头双向循环列表的节点就完成了。
2、迭代器的实现
template<class T,class Ref>
struct _list_iterator {
typedef list_node<T> node;
typedef _list_iterator<T,Ref> self;
node* _node;
_list_iterator(node* node)
:_node(node)
{}
self& operator++() {
_node = _node->next;
return *this;
}
self& operator--() {
_node = _node->prev;
return *this;
}
Ref operator*() {
return _node->_data;
}
bool operator!=(const self& s)const {
return _node != s._node;
}
};
list的迭代器不再是原生指针,它需要单独创建一个类来实现它作为迭代器的功能,并且list本质是链表,所以需要迭代器来进行遍历,所以要先完成迭代器的功能。
思路:
迭代器的成员变量也是一个节点node,遍历是通过节点中存储的其他节点的指针来进行遍历。迭代器需要完成两个,分别是可以修改的迭代器和不可修改的const迭代器。为了防止代码的冗余,使用类模板来完成const和非const迭代器。
构造函数使用一个node节点来进行初始化。
之后通过运算符的重载来完成迭代器的遍历。
const和非const迭代器唯一的区别就是解引用的返回值,所以只需要在解引用的返回值那使用模板即可。
typedef _list_iterator<T,T&> iterator;
typedef _list_iterator<T,const T&> const_iterator;
iterator begin() {
return _head->next;
}
iterator end() {
return _head;
}
const_iterator begin()const {
return _head->next;
}
const_iterator end()const {
return _head;
}
迭代器所必须的还有begin和end函数,也需要const和非const
begin返回哨兵位的下一个节点,end返回哨兵位就可以。
3、insert
iterator insert(iterator pos, const T& val) {
node* newnode = new node(val);
node* cur = pos._node;
node* prev = cur->prev;
newnode->next = cur;
cur->prev = newnode;
newnode->prev = prev;
prev->next = newnode;
return newnode;
}
插入动作是插入在传过来的迭代器的前面进行插入。
思路:
先新建一个节点,之后保存pos迭代器的节点和pos迭代器的前一个节点。将新节点进行插入连接。
4、erase
iterator erase(iterator& pos) {
node* cur = pos._node;
node* next = cur->next;
node* prev = cur->prev;
prev->next = next;
next->prev = prev;
delete cur;
return next;
}
思路:
先保存pos节点和pos的上一个节点prev和下一个节点next,对next和prev、进行连接,之后释放pos节点。
5、析构函数
~list() {
clear();
delete _head;
_head = nullptr;
}
void clear() {
iterator lt = begin();
while (lt != end()) {
lt = erase(lt);
}
}
思路:
析构函数使用clear函数清楚哨兵位之外的所有节点,再单独释放哨兵位就析构完成了。
clear的实现:
使用迭代器进行遍历,将除哨兵位之外的每个节点进行删除。
6、push_back
void push_back(const T& data) {
insert(end(), data);
}
思路:
直接复用insert便可。
7、push_front
void push_front(const T& data) {
insert(begin(), data);
}
思路:
直接复用insert便可。
8、pop_back
void pop_back() {
erase(--end());
}
思路:
直接复用erase便可。
9、pop_front
void pop_front() {
erase(begin());
}
思路:
直接复用erase便可。
10、operator=
list<T>& operator=(list<T>lt) {
swap(lt);
return *this;
}
思路:
使用拷贝构造传过来一个只在该函数生效的对象,与被赋值的对象lt进行交换。
void swap(list<T>& lt) {
node* tmp = _head;
_head = lt._head;
lt._head = tmp;
}
swap只需要交换两个对象的哨兵位就可以实现交换。
测试
template<typename Container>
void print(const Container& lt) {
typename Container::const_iterator lt1 = lt.begin();
for (auto e : lt) {
cout << e << ' ';
}
}
void test2() {
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
lt1.push_back(6);
print(lt1);
list<string> lt2;
cout << endl;
lt2.push_back("11111111");
lt2.push_back("22222222");
lt2.push_back("33333333");
lt2.push_back("44444444");
lt2.push_back("55555555");
lt2.push_back("66666666");
print(lt2);
}
list的功能不仅能支持内置类型,还能够支持自定义类型。
为了防止代码的冗余,这里需要用typename来定义模板,typename是专门用于未实例化的类,能够让代码顺利通过。如果容器未实例化,迭代器是无法访问的,typename就是告诉编译器目前没有实例化,但之后会实例化,让这段代码能够通过。
运行
内置类型和自定义类型都能够使用。
全部代码
头文件
#pragma once
namespace ls {
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)
{
}
};
template<class T,class Ref>
struct _list_iterator {
typedef list_node<T> node;
typedef _list_iterator<T,Ref> self;
node* _node;
_list_iterator(node* node)
:_node(node)
{}
self& operator++() {
_node = _node->next;
return *this;
}
self& operator--() {
_node = _node->prev;
return *this;
}
Ref operator*() {
return _node->_data;
}
bool operator!=(const self& s)const {
return _node != s._node;
}
};
template<class T>
class list {
typedef list_node<T> node;
public:
typedef _list_iterator<T,T&> iterator;
typedef _list_iterator<T,const T&> const_iterator;
iterator begin() {
return _head->next;
}
iterator end() {
return _head;
}
const_iterator begin()const {
return _head->next;
}
const_iterator end()const {
return _head;
}
void empty_init() {
_head = new node;
_head->next = _head;
_head->prev = _head;
}
list()
{
empty_init();
}
list(list<T>& tmp) {
empty_init();
for (auto& e : tmp) {
push_back(e);
}
}
~list() {
clear();
delete _head;
_head = nullptr;
}
void clear() {
iterator lt = begin();
while (lt != end()) {
lt = erase(lt);
}
}
void push_back(const T& data) {
insert(end(), data);
}
void push_front(const T& data) {
insert(begin(), data);
}
void pop_back() {
erase(--end());
}
void pop_front() {
erase(begin());
}
iterator insert(iterator pos, const T& val) {
node* newnode = new node(val);
node* cur = pos._node;
node* prev = cur->prev;
newnode->next = cur;
cur->prev = newnode;
newnode->prev = prev;
prev->next = newnode;
return newnode;
}
iterator erase(iterator& pos) {
node* cur = pos._node;
node* next = cur->next;
node* prev = cur->prev;
prev->next = next;
next->prev = prev;
delete cur;
return next;
}
void swap(list<T>& lt) {
node* tmp = _head;
_head = lt._head;
lt._head = tmp;
}
list<T>& operator=(list<T>lt) {
swap(lt);
return *this;
}
private:
node* _head;
};
template<typename Container>
void print(const Container& lt) {
typename Container::const_iterator lt1 = lt.begin();
for (auto e : lt) {
cout << e << ' ';
}
}
void test2() {
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
lt1.push_back(6);
print(lt1);
list<string> lt2;
cout << endl;
lt2.push_back("11111111");
lt2.push_back("22222222");
lt2.push_back("33333333");
lt2.push_back("44444444");
lt2.push_back("55555555");
lt2.push_back("66666666");
print(lt2);
}
}
cpp文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include"list.h"
int main() {
ls::test2();
}
总结
本文简单实现了list的功能,对于list有了更深入的了解。