当前位置:网站首页>list的模拟实现
list的模拟实现
2022-07-25 07:18:00 【'派派'】
目录
1.介绍
2.模拟实现
先看listd的实现原理,本质是一个带头双向循环链表。如图

2.1先构造Node结点
template<class T>
struct list_node
{
list_node<T>* _next;
list_node<T>* _prev;
T _data;
list_node(const T& val = T())//缺省的构造函数
:_next(nullptr)
, _prev(nullptr)
, _data(val) //存储数据
{}
};2.2构造迭代器
接下来要对结点进行操作,完成遍历,增删查改等等,可再构造一个结构体,存储上面Node的指针。也就是一个迭代器。如图
template<class T>
struct __list_iterator
{
typedef list_node<T> Node; //将上面结点重命名Node
typedef __list_iterator<T> self;//将这个结点命名为self
Node* _node; //存储上面结点的指针
__list_iterator(Node* node)
:_node(node)
{}
};对结点进行一些操作,通过迭代器完成。
T& operator*() //该迭代器不是原生指针,需重载*
{
return _node->_data;//
}
self& operator++() //前置++,返回下一个结点的地址
{
_node = _node->_next;
return *this;
}
self operator++(int)//后置++
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
bool operator==(const self& it)
{
return _node == it._node;
}
2.3构造list容器
list类中存储有结构体Node的地址
template<class T>
class list
{
public:
typedef __list_iterator<T> iterator;
typedef list_node<T> Node;
list()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
iterator begin()//
{
return iterator(_head->_next);//_head->next是指向首元素的,类型是Node<T>*,用
itreator接收了
//return _head->_next;//单参构造函数支持隐式类型的转换
}
iterator end()
{
return iterator(_head);
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
Node* _head;//存储结点的地址
};
通过上面的代码,就基本完成了一个list了。下面可以验证一下。

但假设T是自定义类型的话,例如:
struct AA
{
AA(int a1 = 0, int a2 = 0)
:_a1(a1)
, _a2(a2)
{}
int _a1;
int _a2;
};
void test_list2()
{
list<AA> lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
lt.push_back(AA(4, 4));
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
cout << (*it)._a1 << "-"<< (*it)._a2 <<" ";//目前只能这样访问元素
++it;
}
cout << endl;
}即使重载了解引用--*,不能直接访问,因为*it 拿到的是 AA类型的数据。所以可以在重载一个->符号。
T* operator->()
{
//return &(operator*());
return &_node->_data;
}
cout << it->_a1 << "-" << it->_a2 << " ";但其实it->是指AA*,it->->本质是这样访问才是正确的,不过编译器做了优化。
2.4const_iterator的实现
例如:
void print_list(const list<int>& lt)
{
list<int>::const_iterator it = lt.begin();
while (it != lt.end())
{
//*it = 10; // 不允许修改
cout << *it << " ";
++it;
}
cout << endl;
}需要在写一个const_iterator的实现,那是不是要重新再写一份struct __list_iterator{},再对里面内容进行适当修改。其实二者有许多地方相似,可通过模版实现。
对list容器部分代码进行改动
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;
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);
//return _head->_next;
}
iterator end()
{
return iterator(_head);
}
...........
...........//其余函数实现
...........
}再对迭代器部分代码改动:
template<class T, class Ref, class Ptr>//Ref是T&,Ptr是T*,或者Ref是const T&,Ptr是
const T*,根据传过来的类型进行确定
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 &(operator*());
return &_node->_data;
}
........
........//其余函数的实现
........
}//Ref是T&,Ptr是T*, 或者 Ref是const T&,Ptr是 const T*,根据传过来的类型进行确定
看图分析:


2.5其余函数的实现
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
template <class InputIterator>
list(InputIterator first, InputIterator last)//用一段迭代器区间完成构造
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
}
// lt2(lt1) -- 现代写法
list(const list<T>& lt)
{
empty_init();
list<T> tmp(lt.begin(), lt.end());
swap(tmp);
}
// lt2 = lt1
list<T>& operator=(list<T> lt)//值传递
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
//Node* tail = _head->_prev;
//Node* newnode = new Node(x);
_head tail newnode
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
// 插入在pos位置之前
iterator insert(iterator pos, const T& x)
{
Node* newNode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = cur;
cur->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}补充:由上面代码可知,当插入新元素后,pos依旧指向原来的元素,但函数是返回指向新元素的指针。当删除元素后,当前pos所指向的空间已被销毁,函数返回的是被删除元素的下一个元素的地址。
边栏推荐
- %d,%s,%c,%x
- 微信小程序wx.request接口
- Day by day, month by month | Shenzhen potential technology released the extreme accelerated version of molecular docking engine uni docking
- [daily question] sword finger offer II 115. reconstruction sequence
- 蔚来一面:多线程join和detach的区别?
- The idea of the regular crawler of the scratch
- UXDB怎么从日期值中提取时分秒?
- Save the sqoop run template
- 【每日一题】1184. 公交站间的距离
- js无法获取headers中Content-Disposition
猜你喜欢

【terminal】x86 Native Tools Command Prompt for VS 2017

Xinku online | cnopendata shareholder information data of A-share listed companies

Beijing internal promotion | Microsoft STCA recruits nlp/ir/dl research interns (remote)

北京内推 | 微软STCA招聘NLP/IR/DL方向研究型实习生(可远程)

9大最佳工程施工项目管理系统

Rambus announces ddr5 memory interface chip portfolio for data centers and PCs

How can dbcontext support the migration of different databases in efcore advanced SaaS system

【每日一题】1184. 公交站间的距离
![[cloud native] the ribbon is no longer used at the bottom of openfeign, which started in 2020.0.x](/img/7e/1d27e3f1856ab8c6cbfc5221c717bb.png)
[cloud native] the ribbon is no longer used at the bottom of openfeign, which started in 2020.0.x

With apple not making money, the 2trillion "fruit chain" abandons "fruit" and embraces "special"
随机推荐
[cloud native] the ribbon is no longer used at the bottom of openfeign, which started in 2020.0.x
Go basic notes_ 5_ Process statement
With apple not making money, the 2trillion "fruit chain" abandons "fruit" and embraces "special"
CTF Crypto---RSA KCS1_ Oaep mode
一日千里,追风逐月 | 深势科技发布极致加速版分子对接引擎Uni-Docking
Purpose of SQL square brackets
Elasticserach里delete_by_query的机制是什么?
Summary of learning notes of deep learning application development (II)
vulnhub CyberSploit: 1
【程序员2公务员】三、资源搜集
What are the types of financial products in 2022? Which is suitable for beginners?
What if Oracle 19C migration encounters large lob tables?
Price reduction, game, bitterness, etc., vc/pe rushed to queue up and quit in 2022
[notes for question brushing] search the insertion position (flexible use of dichotomy)
Xinku online | cnopendata shareholder information data of A-share listed companies
QT6 with vs Code: compiling source code and basic configuration
Default value of dart variable
New functions of shixizhi are online. These new functions are online in June. Can you use them?
The relationship between Informatics, mathematics and Mathematical Olympiad (July 19, 2022) C
js无法获取headers中Content-Disposition