STL-list模拟实现
#pragma once
#include"16my_Itetator.h" //测试用
#include<iostream> //测试用
using std::cout;
using std::endl;
using std::cin;
namespace test
{
//struct默认权限是public,一般也不会加权限,class才会(需要封装时使用class)
//结点使用struct的好处是开放结点,可以直接访问.不用重载流插入(stl也没重载), template<class T>
struct list_node
{
//一个完整的结点,包含前,后,和自己
list_node<T>* _next;
list_node<T>* _prev;
T _data;// //使用全缺省匿名对象(只要是模板,都得传对象,自定义类型接收的必须是实体对象)构造
//list中使用是分配器分配空间,所以没写构造函数
list_node(const T& x = T())//此处为方便不使用分配器,直接初始化个匿名对象
: _next(nullptr)
, _prev(nullptr)
, _data(x)
{} //移动构造
list_node(T&& x = T())//此处为方便不使用分配器,直接初始化个匿名对象
: _next(nullptr)
, _prev(nullptr)
, _data(std::forward<T>(x))
{} //拷贝原理 /**
*
* list<int> lt(1) 初始化过程:
* 先走拷贝构造,
* 走init建一个头结点
* 然后insert, insert中会建一个新结点, 值为1, 完成插入
*
* list<list<int>> lt1 初始化过程
* 走默认构造, 空初始化一个list<T>头结点的过程(newnode之前), 再走默认构造, 建一个list<int>结点的匿名对象, 走的是拷贝构造(匿名对象传给x),
* 将匿名对象拷贝进去.析构匿名对象, list<list<int>>默认构造完成
*
* list<list<int>> lt2(lt1) 初始化过程
* 走拷贝构造, empty(就是头结点过程), ... 和lt1初始化过程一样
* 建一个临时变量(接收迭代器构造的list)走迭代器构造, empty(建一个lt1的头结点), push_back
* 交换头和临时头的内容, 完成拷贝构造
*
* 在第一层迭代器构造中, 将lt1插进的过程有两层拷贝构造
* 第一层拷贝构造 -- x == lt1
* push过程newnode(x == lt)->第二层拷贝构造:--通过初始化列表走的 解决问题
*
* 综合发现 : 多重结点类只需要完成第一层深拷贝, 即可完成多层深拷贝
* 因为每次都会new新结点, 且初始化列表赋值过程不是值拷贝, 而是会调用类类型的构造函数
*/ }; //这里声明的是list专用的迭代器 -- 实例化后能用于访问list
//迭代器用于访问结点
template<class T, class Ref, class Ptr>//Ref会接收参数 -- Ref会接收T&和const T&等,一个模板解决
struct __list_iterator
{
//成员类型
typedef list_node<T> node;
typedef __list_iterator<T,Ref,Ptr> self; //只管接收 //成员变量
node* _node; //成员变量只需要1个结点指针,通过这个结点指针的各种操作来模拟指针 //通过结点的指针来构造迭代器,模仿指针
__list_iterator(node* n)
:_node(n)
{}
//不用写拷贝构造,直接值拷贝就行了,需要的就是值拷贝,指向同一个结点 //list迭代器不用写析构函数,因为迭代器接收的是list结点,由list负责即可 //指针操作运算符重载,模拟指针 //解引用重载能够模拟指针返回的是结点的data,和string,vector迭代器一样
//
/**
* 模拟指针的方法
* 我们对list的迭代器来说,
* 为了符合习惯的指针用法
* 对于结构体,实体可以通过成员访问修饰符去访问成员
*
* 设struct AA{_a1,_a2};如AA._a1 , (&AA)->_a1
* list<AA> iterator it = lt.begin();要想list<AA>能实现通过list迭代器实现上述功能,我们对迭代器运算符进行重载
* 由于我们迭代器模拟的是元素的指针,list中的元素是AA,所以让it模拟成AA的指针:&AA
* 我们希望迭代器能做到和基本类型一样有(*it)._a1 ,it->_a1;
* 所以,我们将迭代器重载为
* *it 返回 AA //然后it便可以实现(*it)._a1;
* it-> 返回 &AA //目的是it可以实现it->_a1 ,实际上要访问_a1的过程是it->->_a1,但这样并不协调,为了可读性,所以编译器会将其优化成为it->_a1;
*
* 可以发现,要实现it->并不容易,it毕竟是个结构体,这么实现是个很好的思路
*
* 运算符重载的其中一个目的是可读性要强
*
*/ Ref operator*()
{
return _node->_data;
}
//箭头重载很难理解,编译器做了一定优化,后续再学习
//这个重载是会有编译器优化的 Ptr 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& x)
{
return _node != x._node;
}
bool operator==(const self& x)
{
return _node == x._node;
}
}; //list_const_iterator : const迭代器需要另写一个类 -- 优化:增加模板参数
//template<class T>
//struct __list_const_iterator
//{
// typedef list_node<T> node;
// typedef __list_const_iterator<T> self;
// node* _node;
// __list_const_iterator(node* n)
// :_node(n)
// {} // const T& operator*()
// {
// return _node->_data;
// }
// const T* operator->()
// {
// return &(_node->date);
// } // self& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// self operator++(int)
// {
// self tmp = *this;
// _node = _node->_next;
// return tmp;
// }
// self& operator--()
// {
// _node = (*_node)._next;
// return *this;
// }
// self operator--(int)
// {
// self tmp = *this;
// _node = _node->_next;
// return tmp;
// }
// bool operator!=(const self& x)
// {
// return _node != x._node;
// }
// bool operator==(const self& x)
// {
// return _node == x._node;
// }
//}; template<class T>
class list
{
private:
typedef list_node<T> node;
public:
//正向迭代器类型重命名
typedef __list_iterator<T,T&,T*> iterator;
typedef __list_iterator<T,const T&,const T*> const_iterator; //反向迭代器类型重命名
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<iterator, const T&, const T*> const_reverse_iterator;
//tese
//static int Swap;
//static int destruct;
//static int itstruct;
//static int cpstruct; //iterator
//返回迭代器,目的是按规范通过迭代器访问结点
//虽然迭代器不是原生指针,但是list迭代器重载了解引用运算符*,可以和指针一样解引用就能得到数据
iterator begin()
{
//不能直接返回链表头结点的下一个的指针,因为链表的存储空间不连续,不能对指针直接加加减减,所以需要迭代器 //需要实例化迭代器后才能使用迭代器
//iterator it(_head->_next);
//return it; //可以使用C++提供的匿名对象来简化代码
return iterator(_head->_next);//正向迭代器类接收链表指针构造出正向迭代器实体
}
const_iterator begin() const //const修饰的*this,也就是链表,所以链表不能修改
{
return const_iterator(_head->_next);
}
iterator end()
{
//返回链表头结点的迭代器
return iterator(_head);
}
const_iterator end()const
{
return const_iterator(_head);
} //反向迭代器
reverse_iterator rbegin() //
{
return reverse_iterator(end());//返回反向迭代器(适配器)接收正向迭代器的对象构造出反向迭代器
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(begin());
} //list操作这边和数据结构的带头双向循环链表差不多,重点是iterator.写操作前写将迭代器整出来
private:
node* _head;//链表的操作就是从一个头开始,所以链表的成员就一个头指针
public:
//构造函数目前只写三个,1.无参,只创建头结点 2.迭代器构造 3.初始化n个结点 (不想写) //只构造了一个头结点
list()
{
//cout << "list(" << this <<")" << endl;
empty_init();
}
list(const T& x)
{
empty_init();
push_back(x);
}
//list(const T& x)
//{
// empty_init();//建一个头结点
// //...
// push_back(x);
//} //全缺省多个结点构造
//list(size_t n , const T& x = T())
//{
// cout << "list(size_t n , const T& x = T(" << this << ")))" << endl;
// empty_init();//建一个头结点
// //...
// while (n)
// {
// push_back(x);
// --n;
// }
//}
//list(int n , const T& x = T())
//{
// cout << "list(int n , const T& x = T(" << this << ")))" << endl;
// empty_init();//建一个头结点
// //...
// while (n)
// {
// push_back(x);
// --n;
// }
//} //迭代器构造
//深拷贝核心:只要进迭代器,就可以深拷贝
template <class InputIterator> //可以接收任意类型的迭代器,不只是自己的迭代器
list(InputIterator first, InputIterator last)
{
//cout << "list(InputIterator first, InputIterator last) " << this << ")" << endl;
empty_init();//建一个头结点
while (first != last)
{
push_back(*first);//"new"一个结点,拷x进来,++first,再new一个结点,拷x进来,通过这样实现了深拷贝,new关键字容易忽略,就是malloc
++first;
}
}
void swap(list<T>& tmp)
{
//cout << "list(InputIterator first, InputIterator last)" << this << ")" << endl;
std::swap(_head, tmp._head);
}
//拷贝构造
list(const list<T>& lt)
{
//深拷贝时,第一次拷贝构造是list<list<int>>,因为构造一个匿名对象,此时x是list<int> , 第二次是list<int>,拷贝构造给此时 empty_init();
list<T> tmp(lt.begin(), lt.end()); //深拷贝
swap(tmp);
} //此处不是深拷贝核心 -- 赋值初始化时的深拷贝
list<T>& operator=(list<T> lt) //:传值传参->拷贝构造+迭代器构造,深拷贝
//赋值运算符重载:操作数是list<list<>>,在非初始化时走这里,lt1 = lt2
//使用到的场景,直接赋值给另一个对象:lt1 = lt2 :
{
//cout << "operator="<<endl;
swap(lt);
return *this;
} //移动构造
list(list&& lt)
{
empty_init();
list<T> tmp(lt.begin(), lt.end()); //深拷贝
swap(tmp);
} void empty_init()
{
//cout << "empty_init()" << this << ")" << endl;
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
} ~list()
{
//cout << "~list()" << this << ")"<<endl;
clear();
delete _head;
_head = nullptr;
}
//clear:保留头结点,其他全部清空
void clear()
{
iterator it = begin(); //begin用于初始化it,调用默认生成的值拷贝拷贝构造
while (it!=end())
{
//it = erase(it);
erase(it++);
}
} //Capacity
//size实现需要迭代器计算链表长度会用就行 //modifiers
//插入
//这里的传进来的迭代器是list的迭代器
void insert(iterator pos , const T& x ) //insert没有迭代器失效问题,pos依然指向同一个结点
{
//cout << "insert" << *this << ")" << endl;
node* cur = pos._node;
node* prev = cur->_prev;
node* new_node = new node(x); //new阿!!!!!!!!!!!!!!!!!new是开辟新结点,然后再把x赋值进去--直接实现了深拷贝6666 prev->_next = new_node;
new_node->_prev = prev;
new_node->_next = cur;
cur->_prev = new_node;
} //移动构造
void insert(iterator pos, T&& x) //insert没有迭代器失效问题,pos依然指向同一个结点
{
//cout << "insert" << *this << ")" << endl;
node* cur = pos._node;
node* prev = cur->_prev;
node* new_node = new node(std::forward<T>(x)); //new阿!!!!!!!!!!!!!!!!!new是开辟新结点,然后再把x赋值进去--直接实现了深拷贝6666 prev->_next = new_node;
new_node->_prev = prev;
new_node->_next = cur;
cur->_prev = new_node;
} //删除
iterator erase(iterator pos) //迭代器失效(野指针),返回链表下一个位置的迭代器
{
node* prev = pos._node->_prev;
node* next = pos._node->_next; prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
//尾插
void push_back(const T& x) //插入必须有具体值,总不能插入一个匿名对象
{
//cout << "push_back" << this << ")" << endl;
//node* n = new node(x);
//node* tail = _head->_prev;
//n->_prev = tail;
//tail->_next = n;
//n->_next = _head;
//_head->_prev = n;
insert(end(), x);
} //右值引用版本
void push_back(T&& x)
{
insert(end(), std::forward<T>(x));
} //头插
void push_front(const T& x)
{
//node* n = new node(x);
//
//n->_next = _head->_next;
//_head->_next->_prev = n;
//n->_prev = _head;
//_head->_next = n;
insert(begin(), x);
}
//头删
void pop_front()
{
//node* cur = _head->_next ; //记录要删除的当前结点
//cur->_next->_prev = _head;
//_head->_next = cur->_next;
//delete cur;
erase(begin());
}
//尾删
void pop_back()
{
//node* cur = _head->_prev;
//cur->_prev->_next = _head;
//_head->_prev = cur->_prev;
//delete cur;
erase(--end());
} };
//std::ostream& operator<<(std::ostream& out ,list<> )
//{ //} //测试用例 //深拷贝验证
void test_list1()
{
//using namespace std;
list<int> lt(1);
lt.push_back(2);
//lt.push_back(3);
//lt.push_back(4);
//for (auto x : lt)
//{
// cout << x << endl;
//}
//list<int>::iterator it = lt.begin();
//while (it != lt.end())
//{
// cout << *it << endl;
// ++it;
//}
list<list<int>> lt1;
lt1.push_back(lt);
//lt1.push_back(list<int>(1,1));
//lt1.push_front(list<int>(1));
//list<list<int>>::iterator it = lt1.begin();
//lt1.insert(it, list<int>(2));
//lt1.erase(it);
//list<list<int>> lt2(lt1);
//list<list<int>> lt2 = lt1;
//lt2.push_back(list<int>(2));
//lt2.push_back(list<int>(2));
//lt2.push_back(list<int>(2));
//lt2.pop_back();
//lt2.pop_front(); //list<int> lt1(1,1);
//lt1.push_back(2);
//list<int>::iterator it = lt1.begin();
//cout << *it; } //迭代器const 运算符重载
void print_list(const list<int>& lt)
{ list<int>::const_iterator it = lt.begin();
while (it != lt.end())
{
//++(*cit); std::cout << *it << std::endl;
++it;
}
}
void test_list2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
print_list(lt);
} //迭代器运算符*,->重载,多模板参数
struct AA
{
int _a1;
int _a2; AA(int a1 = 0, int a2 = 0)
:_a1(a1)
, _a2(a2)
{}
};
void test_list3()
{
list<AA> lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3)); // AA* ptr
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
//cout << (*it)._a1 << ":" << (*it)._a2 << endl;
std::cout << it->_a1 << ":" << it->_a2 << std::endl;
//cout << it.operator->()->_a1 << ":" << it.operator->()->_a1 << endl;
++it;
}
std::cout << std::endl;
} void test_list4()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
//list<int>::iterator it = lt1.begin();
//while (it != lt1.end())
//{
// cout << *it << " ";
// ++it;
//}
cout << endl<< "=============" << endl;
list<int>::reverse_iterator rit = lt1.rbegin();
while (rit != lt1.rend())
{
cout << *rit << " ";
++rit;
} } }
STL-list模拟实现的更多相关文章
- <泛> STL - vector 模拟实现
今天为大家带来一个模拟STL-vector的模板实现代码. 首先看一下测试结果,之后再为大家呈现设计 测试效果 测试代码 #include<iostream> #include<ve ...
- 洛谷 P1739 表达式括号匹配【STL/stack/模拟】
题目描述 假设一个表达式有英文字母(小写).运算符(+,-,*,/)和左右小(圆)括号构成,以"@"作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返 ...
- <泛> STL - stack 模拟实现
今天,看C++Template的时候看到那人写了一个Stack,于是乎,手痒,自己也写了一个,在拜读了STD文件和C++模板元编程某些小节之后,你们就看到了这篇代码. 经过上述一番经历之后,我重新写了 ...
- STL string 模拟
下面的代码来自c++ primer plus第5版第12章,书中代码写的非常好: // string1.h -- fixed and augmented string class definition ...
- UVa 12219 Common Subexpression Elimination (stl,模拟,实现)
一般来说,把一颗子树离散成一个int,把一个结点的字符离散成一个int会方便处理 直接map离散.当然一个结点最多只有4个小写字母,也可以直接编码成一个27进制的整数,舍掉0,为了区分0和0000. ...
- 【STL+模拟】UVa 506 - System Dependencies
System Dependencies Components of computer systems often have dependencies--other components that m ...
- hiho一下 第四十七周 拓扑排序一 【静态数组链式前向星存储结构实现 + 拓扑跳出 】
题目1 : 拓扑排序·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 由于今天上课的老师讲的特别无聊,小Hi和小Ho偷偷地聊了起来. 小Ho:小Hi,你这学期有选什么 ...
- 【数据结构】洛谷2019 OI春令营 - 普及组 作业
[P3662][USACO17FEB]Why Did the Cow Cross the Road II S 求解连续的k个数的最大值,利用前缀和维护即可. #include<bits/stdc ...
- 基于各种基础数据结构的SPFA和各种优化
一.基于各种数据结构的SPFA 以下各个数据均为不卡SPFA的最短路模板:P3371 [模板]单源最短路径(弱化版)的测试时间 1.STL队列:用时: 1106ms / 内存: 8496KB #inc ...
- LeetCode双周赛#36
1604. 警告一小时内使用相同员工卡大于等于三次的人 题目链接 题意 给定两个字符串数组keyName和keyTime,分别表示名字为keytime[i]的人,在某一天内使用员工卡的时间(格式为24 ...
随机推荐
- React中函数组件与类组件的两种使用
React 创建组件的两种方式 函数组件:使用js函数创建的组件 约定1:函数名称必须以大写字母开头 约定2:函数组件必须要有返回值. 如果返回值为null.表示不渲染任何内容. return nul ...
- 一个Promise指定多个成功或者失败的回调详解
// 当一个Promise指定多个成功或者失败的回调:都会调用吗? 会的 let p = new Promise((resolve, reject) => { resolve('第一种成功1') ...
- Mysql索引失效场景
Mysql索引失效场景 序言 众所周知在Mysql中,通过使用索引的方式可以加快查询速度,从而避免全文搜索:而索引本身就像图书馆中所有书籍目录,通过查询关键字就能快速找到目标书籍在几列几行,这 ...
- 6.3 Windows驱动开发:内核枚举IoTimer定时器
今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核IoTimer定时器,内核定时器其实就是在内核中实现的时钟,该定时器的枚举非常简单,因为在IoInitializeTimer初始化 ...
- Centos8 配置IP地址与阿里YUM源
Centos8 系统中无法找到network.service网络服务,默认已经被nmcli替换了,所以修改方式略微变化,在/etc/sysconfig/network-scripts/里也看不到任何脚 ...
- KMP 学习笔记
前言-- \(char\) 与 \(string\) 有的时候 \(char\) 数组确实比 \(string\) 好用,且字符串长度很大时 \(string\) 会被卡掉,所以不要犯懒,老实用 \( ...
- P4402 [Cerc2007] robotic sort 机械排序题解
题目链接:[Cerc2007] robotic sort 机械排序 前置知识点:文艺平衡树 具体的我们会将序号下标作为平衡树的键值,这样一来每个节点其实就是数组中的每个位置,又因为这个位置是具有有序性 ...
- linux 后台运行进程:& , nohup
目录 后台执行 & nohup 查看后台运行的命令 jobs ps 关闭当前后台运行的程序 kill 前后台进程的切换与控制 ctrl + z 命令 fg 命令 bg 命令 思考 问题1-为什 ...
- 洛谷P1009 阶乘之和
捏妈第三节的题单名不是循环结构吗,直接出了第八节的高精度大数计算,紧急学习 对于较大数的加减乘除阶乘等,C/C++原生的数据类型是存储不了的(即便用longlong),直接计算会出现数据移除成负数的结 ...
- offline 2 online | Cal-QL:校准保守 offline 训出的 Q value,让它与真实 reward 尺度相当
论文标题:Cal-QL: Calibrated Offline RL Pre-Training for Efficient Online Fine-Tuning. NeurIPS 2023,5 5 6 ...