C++中的list类模拟实现

目录

list类模拟实现

list类节点结构设计

list类非const迭代器结构设计

迭代器基本结构设计

迭代器构造函数

operator++()函数

operator*()函数

operator!=()函数

operator++(int)函数

operator--()函数

operator--(int)函数

operator==()函数

operator->()函数

list类const迭代器结构设计

迭代器基本结构设计

迭代器构造函数

operator++()函数

operator*()函数

operator!=()函数

operator++(int)函数

operator--()函数

operator--(int)函数

operator==()函数

operator->()函数

const版本的迭代器使用问题

const版本迭代器和非const版本迭代器合并优化

list类无参构造函数

list类析构函数

list类拷贝构造函数

list类赋值运算符重载函数

begin()函数

end()函数

insert()函数

push_back()函数

push_front()函数

erase()函数

pop_back()函数

pop_front()函数

clear()函数

额外补充

项目代码

list类实现文件

测试文件


list类模拟实现

list类节点结构设计

因为list类为本质是带头双向循环链表,所以在设计list类时,需要先设计节点的结构,并且因为每一个节点是一个整体,所以可以考虑将每一个节点的初始化构造放在结构体中,如下代码所示:

//节点结构
template<class T>
struct _list_node
{T data;// 数据类型_list_node* prev;// 前驱指针_list_node* next;// 后继指针//节点构造函数_list_node(const T& x = T()):data(x),prev(nullptr),next(nullptr){}
};

list类非const迭代器结构设计

因为list类是带头双向循环链表,所以迭代器不能单单使用普通指针来代替,因为普通指针可以指针的++/--等操作可以作用到连续空间,链表两个节点空间不一定连续,所以额外重载++/--等运算符

迭代器基本结构设计

//迭代器结构
template<class T>
struct _list_iterator
{typedef _list_node<T> Node;typedef _list_iterator<T> self;Node* _node;//迭代器节点
};

迭代器构造函数

为了可以将普通指针看作迭代器,需要构造函数使用普通指针进行构造

//迭代器构造函数
_list_iterator(Node* node):_node(node)
{}

考虑下面的代码

void test()
{sim_list::list<int>::iterator it = ls.begin();while (it != ls.end()){cout << *it << " ";++it;}
}

在上面的测试代码中,当使用迭代器遍历时,需要使用到3个运算符

  1. !=:不等于运算符
  2. *:解引用运算符
  3. ++:前置自增运算符

但是由于it不是原生指针(内置类型的指针),所以需要额外重载这三个运算符

operator++()函数

重载前置++运算符的本意是让迭代器可以从当前节点移动到下一个节点,所以只需要移动节点类型的指针指向下一个节点即可

//迭代器前置++运算符重载
self& operator++()
{_node = _node->next;return *this;
}

operator*()函数

重载*运算符本意是为了获取当前有效数据节点数据域中的值,所以返回当前节点数据域的值即可

//迭代器*运算符重载
T& operator*()
{return _node->data;
}

operator!=()函数

重载!=运算符本意是为了判断迭代器指向的当前节点是end()迭代器的位置(即是否已经遍历完链表)

//迭代器!=运算符重载
bool operator!=(const self& cur)
{return _node != cur._node;
}

operator++(int)函数

后置++运算符重载需要满足先使用再自增,所以需要提前记录当前节点再改变当前节点指向为下一个节点

//迭代器后置++运算符重载
self operator++(int)
{Node* cur = _node;_node = _node->next;return cur;
}

operator--()函数

前置--运算符重载直接返回上一个节点的位置即可

//迭代器前置--运算符重载
self& operator--()
{node = node->prev;return *this;
}

operator--(int)函数

后置--运算符重载需要满足先使用再自增,所以需要提前记录当前节点再改变当前节点指向为下一个节点

//迭代器后置--运算符重载
self operator--(int)
{Node* cur = node;node = node->prev;return cur;
}

operator==()函数

重载==运算符为了判断当前节点是否等于指定节点

//迭代器==运算符重载
bool operator==(const self& cur)
{return node == cur.node;
}

operator->()函数

如果模板参数为自定义类型时,访问自定义类型的成员变量时除了可以解引用之后通过.成员变量的方式以外,还有->成员变量的方式,但是对于list类来说不存在原生指针,只有迭代器,所以迭代器需要重载->运算符,考虑下面的代码

struct test
{int num1;int num2;
};void test_operatorTo()
{sim_list::list<struct test> ls;sim_list::list<struct test>::iterator it = ls.begin();//使用直接访问cout << (*it).num1 << " " << (*it).num2 << endl;//使用间接访问cout << it->num1 << " " << it->num2 << endl;
}

为了可以使用->运算符,需要重载->运算符

//迭代器->运算符重载
T* operator->()//返回对应类型的指针
{return &(node->data);//获取当前数据域的地址
}

所以上面的代码可以转化为

cout << it.operator->()->num1 << " " << it.operator->()->num2 << endl;
//其中it.operator->()返回自定义类型变量的地址

list类const迭代器结构设计

设计const迭代器的思路和非const迭代器思路基本一致,但是设计const迭代器不可以单单是在已有的iterator前面加上const改为const iterator,这种写法会使编译器认为是T* const,此时实现的效果是,迭代器指向的内容可以修改,但是指向不可以修改,而需要的const迭代器实现的效果是迭代器指向的内容不可以修改,但是指向可以修改,即const T*,所以需要单独设计一个const_iterator类来解决这个问题

迭代器基本结构设计

template<class T>
struct _list_iterator_const 
{typedef _list_node<T> Node;typedef _list_iterator_const<T> self;Node* node;
};

迭代器构造函数

//构造函数
//同非const版本一样,使用指针构造迭代器
_list_iterator_const(Node* node):_node(node)
{}

operator++()函数

因为是改变迭代器指针的指向,所以设计operator++()函数和非const版本的方式相同

//operator++()函数
self& operator++()
{_node = _node->next;return *this;
}

operator*()函数

因为const版本迭代器不可以通过解引用修改指针指向的内容,所以需要使用const修饰返回值

//operator*()函数
const T& operator*() const
{return _node->data;
}

operator!=()函数

对于!=运算符来说,和非const版本思路相同

//operator!=()函数
bool operator!=(const self& cur)
{return _node != cur._node;
}

operator++(int)函数

对于后置++来说与const版本同理

//operator++(int)函数
self& operator++(int)
{Node* cur = _node;_node = _node->next;return cur;
}

operator--()函数

因为--改变的是迭代器指向的内容,所以与非const版本迭代器思路相同

self& operator--()
{_node = _node->prev;return *this;
}

operator--(int)函数

设计后置--的思路与非const版本相同

//operator--(int)函数
self& operator--(int)
{Node* cur = _node;_node = _node->prev;return cur;
}

operator==()函数

设计==运算符重载函数思路和非const版本相同

//operator==()函数
bool operator==(const self& cur)
{return _node = cur._node;
}

operator->()函数

需要满足返回值为const类型即可

const T* operator->()
{return &_node->data;
}

const版本的迭代器使用问题

上面的const版本迭代器实现方式可以应用于对象为const类型时,例如下面的测试代码

void test_const_iterator()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);const sim_list::list<int> ls1(ls);// const对象sim_list::list<int>::const_iterator cit = ls1.begin();// 编译器自动识别const类型的迭代器while (cit != ls1.end()){//*cit = 2;cout << *cit << " ";cit++;}cout << endl;
}

但是如果对象不是const类型,此时上面的代码就会出现错误,例如下面的测试代码

void test_const_iterator()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int>::const_iterator cit = ls.begin();// 编译器无法自动识别const类型的迭代器while (cit != ls.end()){//*cit = 2;cout << *cit << " ";cit++;}cout << endl;
}

在第二个测试代码中,因为citconst类型的迭代器,所以不可以使用非const版本的迭代器,但是此时的begin()end()均为非const版本迭代器,因为对象ls为非const对象

这里提出两种解决方案:

  1. const版本的begin()end()改为cbegin()cend(),此时显式使ls调用const版本的cbegin()cend()

📌

缺点:没有使用到函数重载的优势

//修改const版本的迭代器
//begin()函数——const版本
const_iterator cbegin() const
{return _head->next;
}//begin()函数——const版本
const_iterator cbegin() const
{return _head->next;
}//测试代码修改为
void test_const_iterator()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int>::const_iterator cit = ls.cbegin();// 显式指定const版本迭代器while (cit != ls.cend()){//*cit = 2;cout << *cit << " ";cit++;}cout << endl;
}
  1. const版本的迭代器结构中添加非const对象向const对象转换的构造函数

📌

缺点:没有调用const版本的迭代器

// 非const对象向const对象转换的构造函数
//传入非const对象,为了确保安全,使用const修饰形参
_list_iterator_const(const _list_iterator<T> nonConst):_node(nonConst._node)//使用非const对象中的_node值构造const版本的对象中的_node
{}

const版本迭代器和非const版本迭代器合并优化

在实现非const版本的迭代器时,可以很明显感觉到除了operator*()函数和operator->()函数两个有不同以外,其余均没有不同,而这两个版本中的这两个函数只是返回值类型不同,那么可以考虑通过模板单独控制这两个返回值类型,现在将类型引用T&用模板参数Ref指代,将类型指针T*用模板参数Ptr指代,则const版本迭代器和非const版本迭代器可以合并为下面的代码:

//迭代器结构——复合版本
template<class T, class Ref, class Ptr>
struct _list_iterator
{typedef _list_node<T> Node;typedef _list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器节点//迭代器构造函数_list_iterator(Node* node):_node(node){}//迭代器前置++运算符重载self& operator++(){_node = _node->next;return *this;}//迭代器前置--运算符重载self& operator--(){_node = _node->prev;return *this;}//迭代器后置++运算符重载self operator++(int){Node* cur = _node;_node = _node->next;return cur;}//迭代器后置--运算符重载self operator--(int){Node* cur = _node;_node = _node->prev;return cur;}//迭代器*运算符重载Ref operator*(){return _node->data;}//迭代器->运算符重载Ptr operator->(){return &(_node->data);}//迭代器!=运算符重载bool operator!=(const self& cur){return _node != cur._node;}//迭代器==运算符重载bool operator==(const self& cur){return _node == cur._node;}
};

📌

注意,此处也需要考虑到非const对象向const对象的转换问题,为了更方便解决,采取上面的第一种解决方案,如果直接是const对象调用则不需要考虑这个问题

list类无参构造函数

对于list类来说,因为只有一个头指针作为成员变量,所以无参构造时只需要考虑创建头结点即可

//头节点处理
void empty_init()
{_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;
}//构造函数
list()
{empty_init();
}

list类析构函数

调用clear()函数后将头节点资源清理并置为空即可

//析构函数
~list()
{clear();delete _head;_head = nullptr;
}

list类拷贝构造函数

//拷贝构造函数
list(list<T>& ls)
{empty_init();for (auto num : ls){push_back(num);}
}

list类赋值运算符重载函数

//赋值运算符重载函数
list<T>& operator=(list<T>& ls)
{if (this != &ls){for (auto num : ls){push_back(num);}}return *this;
}

begin()函数

返回第一个有效数据节点的位置即可

//begin()函数——非const版本
iterator begin()
{return _head->next;//隐式类型转换
}//begin()函数——const版本
const_iterator begin() const
{return _head->next;
}

end()函数

返回最后一个头节点的位置即可

//end()函数——非const版本
iterator end()
{return _head;
}//end()函数——const版本
const_iterator end() const
{return _head;
}

insert()函数

插入节点思路参考带头双向循环链表

📌

可以考虑返回当前节点位置防止迭代器失效问题

//insert()函数
iterator insert(iterator position, const T& val)
{//创建新的节点Node* node = new Node(val);//改变节点指针指向//记录当前position位置的节点Node* cur = position._node;//改变新节点的指向node->next = cur;node->prev = cur->prev;//改变当前位置节点的前一个节点的后继指针指向cur->prev->next = node;//改变当前位置节点的前驱指针指向cur->prev = node;++_size;return position;
}

push_back()函数

设计思路参考带头双向循环链表的思路

//push_back()函数
void push_back(const T& val)
{//创建新的节点Node* node = new Node(val);//调用节点结构构造函数//改变节点指针//先记录当前最后一个节点Node* tail = _head->prev;//改变头节点前驱指针为新节点_head->prev = node;//改变最后一个节点的后继指针指向新节点tail->next = node;//改变新节点的指针node->prev = tail;node->next = _head;++_size;
}

push_front()函数

设计思路参考带头双向循环链表的思路

//push_front()函数
void push_front(const T& val)
{//创建新节点Node* node = new Node(val);//改变指针指向//先记录当前第一个节点Node* first = _head->next;//改变新节点的指针指向node->next = first;node->prev = _head;//改变头指针后继指针指向_head->next = node;//改变原始第一个节点的前驱指针指向first->prev = node;++_size;
}

erase()函数

删除节点的思路类似于带头双向链表的删除思路

📌

注意返回删除节点位置的下一个节点的位置防止出现迭代器失效问题

//erase()函数
iterator erase(iterator position)
{//不可以删除头结点assert(position != iterator(_head));//记录要删除的节点的后一个节点Node* cur = position._node->next;//改变要删除的节点的前一个节点的后继指针position._node->prev->next = cur;//改变要删除的节点的后一个节点的前驱指针cur->prev = position._node->prev;//删除当前位置的指针delete position._node;--_size;return cur;
}

pop_back()函数

直接复用erase()函数即可,但是需要注意删除的位置不是end的位置,而是end前一个位置

//pop_back()函数
void pop_back()
{erase(--end());
}

pop_front()函数

直接复用erase()函数即可

//pop_front()函数
void pop_front()
{//复用erase()函数erase(begin());
}

clear()函数

调用erase()函数循环从头删除即可,但是不可以删除头结点

//clear()函数
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

额外补充

打印函数

当链表中的类型为int时,打印函数中的list模板参数直接设置为int即可,例如下面的代码:

//额外补充的函数——标准库中没有
//打印函数
void print_list(const list<int>& ls)
{sim_list::list<int>::const_iterator cit = ls.begin();while (cit != ls.end()){cout << *cit << " ";cit++;}
}

但是,上面的代码在模板参数处写定为int,如果更换类型,此时就需要在打印函数处同时更改类型。

假设现在的链表为如下:

sim_list::list<string> ls;
ls.push_back("111111");
ls.push_back("111111");
ls.push_back("111111");
ls.push_back("111111");

此时必需更改打印函数中的类型为string类型,但是每一次更换类型就要改变函数步骤相对繁琐,如果一个函数中涉及到多个类型的list,此时就无法实现调用打印函数打印链表内容,此时可以考虑使用模板,将list的模板参数设置为模板参数,如下面的代码:

//额外补充的函数——标准库中没有
//打印函数
template<class T>
void print_list(const list<T>& ls)
{sim_list::list<T>::const_iterator cit = ls.begin();while (cit != ls.end()){cout << *cit << " ";cit++;}
}

但是上面的代码没有通过编译,原因在于模板参数,当模板参数在函数模板或者类模板中,编译器开始编译时,可以实现替换,从而生成对应的函数或者类。但是在上面的代码中,const_iterator是一个被typedef的变量,但是编译器并不知道是重命名的变量,反之编译器可能会认为是静态变量,所以此时到底是const_iterator是静态变量还是重命名的变量编译器并不知道,编译器需要在类sim_list中确定const_iterator的类型,从而实现链接,最后再替换模板参数,因为在模板参数还未被替换时,编译器不能进类sim_list中寻找,因为此时类中可能存在未知的内容没有被处理,所以为了确保正常编译通过,此时不可以使用class T作为模板参数,而应该使用typename T,所以上面的代码修改为:

//额外补充的函数——标准库中没有
//打印函数
template<typename T>
void print_list(const list<T>& ls)
{//此处的typename不可以省略,此处的typename是为了告诉编译器这个需要等到模板参数被替换之后再去类中找的变量typename sim_list::list<T>::const_iterator cit = ls.begin();while (cit != ls.end()){cout << *cit << " ";cit++;}
}

另外还有一个问题,上面的打印代码仅仅实现的是list类的内容打印,但是如果此时需要为其他类打印,则需要另外再写一个打印,方式过于繁琐,所以可以考虑为各种类的内容打印写一个通用的函数,此时设计该函数时需要改变函数参数为各种容器,可以考虑使用函数模板,模板参数即为作为函数参数的容器,如下面的代码:

//各种容器内容打印
template<typename container>
void print_container(const container& con)
{typename container::const_iterator it = con.begin();while (it != con.end()){cout << *it << " ";it++;}cout << endl;
}

项目代码

list类实现文件

#pragma once#include <iostream>
#include <cassert>
using namespace std;namespace sim_list
{//节点结构template<class T>struct _list_node{T data;// 数据类型_list_node* prev;// 前驱指针_list_node* next;// 后继指针//节点构造函数_list_node(const T& x = T()):data(x),prev(nullptr),next(nullptr){}};//迭代器结构——非const版本//迭代器结构——复合版本template<class T, class Ref, class Ptr>struct _list_iterator{typedef _list_node<T> Node;typedef _list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器节点//迭代器构造函数_list_iterator(Node* node):_node(node){}//迭代器前置++运算符重载self& operator++(){_node = _node->next;return *this;}//迭代器前置--运算符重载self& operator--(){_node = _node->prev;return *this;}//迭代器后置++运算符重载self operator++(int){Node* cur = _node;_node = _node->next;return cur;}//迭代器后置--运算符重载self operator--(int){Node* cur = _node;_node = _node->prev;return cur;}//迭代器*运算符重载Ref operator*(){return _node->data;}//迭代器->运算符重载Ptr operator->(){return &(_node->data);}//迭代器!=运算符重载bool operator!=(const self& cur){return _node != cur._node;}//迭代器==运算符重载bool operator==(const self& cur){return _node == cur._node;}};#if 0// 迭代器——const版本template<class T>struct _list_iterator_const {typedef _list_node<T> Node;typedef _list_iterator_const<T> self;Node* _node;//构造函数//使用指针构造迭代器_list_iterator_const(Node* node):_node(node){}//非const对象向const对象转换的构造函数//传入非const对象,为了确保安全,使用const修饰形参_list_iterator_const(const _list_iterator<T> nonConst):_node(nonConst._node)//使用非const对象中的_node值构造const版本的对象中的_node{}//operator++()函数self& operator++(){_node = _node->next;return *this;}//operator*()函数const T& operator*() const{return _node->data;}//operator!=()函数bool operator!=(const self& cur){return _node != cur._node;}//operator--()函数self& operator--(){_node = _node->prev;return *this;}//operator--(int)函数self operator--(int){Node* cur = _node;_node = _node->prev;return cur;}//operator++(int)函数self operator++(int){Node* cur = _node;_node = _node->next;return cur;}//operator==()函数bool operator==(const self& cur){return _node == cur._node;}//operator->()函数const T* operator->(){return &_node->data;}};
#endiftemplate<class T> class list{typedef _list_node<T> Node;public:typedef _list_iterator<T, T&, T*> iterator; // 迭代器——非const版本typedef _list_iterator<T, const T&, const T*> const_iterator; // 迭代器——const版本//头节点处理void empty_init(){_head = new Node;_head->prev = _head;_head->next = _head;_size = 0;}//构造函数list(){empty_init();}//析构函数~list(){clear();delete _head;_head = nullptr;}//拷贝构造函数list(const list<T>& ls){empty_init();for (auto num : ls){push_back(num);}}//赋值运算符重载函数list<T>& operator=(list<T>& ls){if (this != &ls){for (auto num : ls){push_back(num);}}return *this;}//begin()函数——非const版本iterator begin(){return _head->next;//通过构造函数进行隐式类型转换}//begin()函数——const版本const_iterator begin() const{return _head->next;}//end()函数——非const版本iterator end(){return _head;}//end()函数——const版本const_iterator end() const{return _head;}//push_back()函数void push_back(const T& val){//创建新的节点Node* node = new Node(val);//改变节点指针//先记录当前最后一个节点Node* tail = _head->prev;//改变头节点前驱指针为新节点_head->prev = node;//改变最后一个节点的后继指针指向新节点tail->next = node;//改变新节点的指针node->prev = tail;node->next = _head;++_size;}//push_front()函数void push_front(const T& val){//创建新节点Node* node = new Node(val);//改变指针指向//先记录当前第一个节点Node* first = _head->next;//改变新节点的指针指向node->next = first;node->prev = _head;//改变头指针后继指针指向_head->next = node;//改变原始第一个节点的前驱指针指向first->prev = node;++_size;}//insert()函数iterator insert(iterator position, const T& val)    {//创建新的节点Node* node = new Node(val);//改变节点指针指向//记录当前position位置的节点Node* cur = position._node;//改变新节点的指向node->next = cur;node->prev = cur->prev;//改变当前位置节点的前一个节点的后继指针指向cur->prev->next = node;//改变当前位置节点的前驱指针指向cur->prev = node;++_size;return position;}//erase()函数iterator erase(iterator position){//不可以删除头结点assert(position != iterator(_head));//记录要删除的节点的后一个节点Node* cur = position._node->next;//改变要删除的节点的前一个节点的后继指针position._node->prev->next = cur;//改变要删除的节点的后一个节点的前驱指针cur->prev = position._node->prev;//删除当前位置的指针delete position._node;--_size;return cur;}//pop_front()函数void pop_front(){//复用erase()函数erase(begin());}//pop_back()函数void pop_back(){erase(--end());}//clear()函数void clear(){iterator it = begin();while (it != end()){it = erase(it);}}//swap()函数void swap(list<T>& ls){std::swap(_head, ls._head);std::swap(_size, ls._size);}private:Node* _head;//有效数据节点个数size_t _size;};//额外补充的函数——标准库中没有//打印函数//template<typename T>//void print_list(const list<T>& ls)//{//    //此处的typename不可以省略,此处的typename是为了告诉编译器这个需要等到模板参数被替换之后再去类中找的变量//    typename sim_list::list<T>::const_iterator cit = ls.begin();//    while (cit != ls.end())//    {//        cout << *cit << " ";//        cit++;//    }//}//各种容器内容打印template<typename container>void print_container(const container& con){typename container::const_iterator it = con.begin();while (it != con.end()){cout << *it << " ";it++;}cout << endl;}
}

测试文件

#include "list.h"
#include <vector>void test_pushback()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int>::iterator it = ls.begin();while (it != ls.end()){cout << *it << " ";it++;}
}void test_pushfront()
{sim_list::list<int> ls;ls.push_front(1);ls.push_front(2);ls.push_front(3);ls.push_front(4);ls.push_front(5);for (auto num : ls){cout << num << " ";}
}void test_insert()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int>::iterator it = ++ls.begin();//sim_list::list<int>::iterator it = find(ls.begin(), ls.end(), 2);ls.insert(it, 6);for (auto num : ls){cout << num << " ";}
}void test_erase()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int>::iterator it = --ls.end();it = ls.erase(it);it = ls.erase(--it);it = ls.erase(--it);it = ls.erase(--it);for (auto num : ls){cout << num << " ";}
}void test_popback()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);ls.pop_back();ls.pop_back();ls.pop_back();ls.pop_back();ls.pop_back();for (auto num : ls){cout << num << " ";}
}void test_popfront()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);ls.pop_front();ls.pop_front();ls.pop_front();ls.pop_front();ls.pop_front();for (auto num : ls){cout << num << " ";}
}void test_clear()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);ls.clear();//ls.pop_back();for (auto num : ls){cout << num << " ";}
}void test_operatorGive()
{sim_list::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);ls.push_back(4);ls.push_back(5);sim_list::list<int> ls1;ls1 = ls;for (auto num : ls1){cout << num << " ";}
}struct test
{int num1;int num2;
};void test_operatorTo()
{sim_list::list<struct test> ls;sim_list::list<struct test>::iterator it = ls.begin();//使用直接访问cout << (*it).num1 << " " << (*it).num2 << endl;//使用间接访问cout << it->num1 << " " << it->num2 << endl;//等价于cout << it.operator->()->num1 << " " << it.operator->()->num2 << endl;
}//void test_const_iterator()
//{
//    sim_list::list<int> ls;
//    ls.push_back(1);
//    ls.push_back(2);
//    ls.push_back(3);
//    ls.push_back(4);
//    ls.push_back(5);
//    //const sim_list::list<int> ls1(ls);// const对象
//
//    //sim_list::list<int>::const_iterator cit = ls.begin();// 编译器自动识别const类型的迭代器
//    sim_list::list<int>::const_iterator cit = ls.cbegin();// 编译器无法自动识别const类型的迭代器
//
//    while (cit != ls.cend())
//    {
//        //*cit = 2;
//        cout << *cit << " ";
//        cit++;
//    }
//    cout << endl;
//}void test_print()
{sim_list::list<string> ls;ls.push_back("111111");ls.push_back("111111");ls.push_back("111111");ls.push_back("111111");sim_list::print_container(ls);vector<string> v;v.push_back("1111");v.push_back("1111");v.push_back("1111");v.push_back("1111");sim_list::print_container(v);
}int main()
{//test_pushback();//test_pushfront();//test_insert();//test_erase();//test_popback();//test_popfront();//test_clear();//test_operatorGive();//test_operatorTo();//test_const_iterator();test_print();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2980790.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

VMware 15 安装centos7虚拟机

1. 安装前准备 1.1 下载centos 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 下载需要版本的centos版本 直达链接 centos7.9 &#xff1a; centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里云 .基础使用的话安装选择这个就行了&#xff0c;大概下载几分钟 2. …

蓝桥杯2024年第十五届省赛真题-小球反弹

以下两个解法感觉都靠谱&#xff0c;并且网上的题解每个人答案都不一样&#xff0c;目前无法判断哪个是正确答案。 方法一&#xff1a;模拟 代码参考博客 #include <iostream> #include <cmath> #include <vector>using namespace std;int main() {const i…

绿城中国北森商业综合推理40分钟28题管理人才盘点领导选拔总经理竞聘考什么?

复杂信息理解批判性评估 策略性推理概念性推理 40分钟题库实时时更新 晋升通过率>95% 绿城人寿移动航油等国企 各维度说明 ①复杂信息理解:洞察文字、图表等资料的能力&#xff0c;能否快速抓住复杂信息中的要点、提取出关键信息 ②批判性评估:批判性质疑的能力&#xff0…

springcloud Ribbon的详解

1、Ribbon是什么 Ribbon是Netflix发布的开源项目&#xff0c;Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的框架。 2、Ribbon能干什么 LB负载均衡(Load Balance)是什么&#xff1f;简单的说就是将用户的请求平摊的分配到多个服务上&#xff0c;从而达…

Python程序设计教案

文章目录&#xff1a; 一&#xff1a;软件环境安装 1.软件环境 2.技巧 3.新建工程项目 二&#xff1a;相关 1.规范 2.关键字 3.Ascll码表 三&#xff1a;语法基础 1.各种符号 1.1 注释 1.2 占位置的 1.3 回车换行 2.输入输出 2.1 输入input 2.2 输出print …

parallels desktop19.3最新版本软件新功能详细介绍

Parallels Desktop是一款运行在Mac电脑上的虚拟机软件&#xff0c;它允许用户在Mac系统上同时运行多个操作系统&#xff0c;比如Windows、Linux等。通过这款软件&#xff0c;Mac用户可以轻松地在同一台电脑上体验不同操作系统的功能和应用程序&#xff0c;而无需额外的硬件设备…

CDN、边缘计算与云计算:构建现代网络的核心技术

在数字化时代&#xff0c;数据的快速传输和处理是保持竞争力的关键。内容分发网络&#xff08;CDN&#xff09;、边缘计算和云计算共同构成了现代互联网基础架构的核心&#xff0c;使内容快速、安全地到达用户手中。本文将探讨这三种技术的功能、相互关系以及未来的发展趋势。 …

3节点ubuntu24.04服务器docker-compose方式部署高可用elk+kafka日志系统并接入nginx日志

一&#xff1a;系统版本: 二&#xff1a;部署环境&#xff1a; 节点名称 IP 部署组件及版本 配置文件路径 机器CPU 机器内存 机器存储 Log-001 10.10.100.1 zookeeper:3.4.13 kafka:2.8.1 elasticsearch:7.7.0 logstash:7.7.0 kibana:7.7.0 zookeeper:/data/zookeep…

探索未来的区块链DApp应用,畅享数字世界的无限可能

随着区块链技术的飞速发展&#xff0c;分布式应用&#xff08;DApp&#xff09;正成为数字经济中的一股强劲力量。DApp以其去中心化、透明公正的特点&#xff0c;为用户带来了全新的数字体验&#xff0c;开创了数字经济的新潮流。作为一家专业的区块链DApp应用开发公司&#xf…

全面了解俄罗斯的VK开户和Yandex投放及内容运营

俄罗斯的VKontakte&#xff08;简称VK&#xff09;和Yandex是两个重要的在线平台&#xff0c;对于希望在俄罗斯市场进行推广的企业来说&#xff0c;了解如何在这些平台上开户和投放广告以及内容运营是非常关键的。 俄罗斯vk广告如何开户&#xff1f; 通过上海上弦进行俄罗斯V…

明日方舟游戏助手:一键完成日常任务 | 开源日报 No.233

MaaAssistantArknights/MaaAssistantArknights Stars: 11.6k License: AGPL-3.0 MaaAssistantArknights 是一款《明日方舟》游戏的小助手&#xff0c;基于图像识别技术&#xff0c;支持一键完成全部日常任务。 刷理智、掉落识别及上传企鹅物流智能基建换班、自动计算干员效率…

c++的策略模式,就是多态

一、定义&#xff1a; 策略模式定义了一系列的算法&#xff0c;并将每一个算法封装起来&#xff0c;而且使它们还可以相互替换。 策略模式让算法独立于使用它的客户而独立变化。 二&#xff0c;核心 抽象策略&#xff08;抽象基类&#xff09;&#xff08;Strategy&#xff09…

面试八股——RabbitMQ

消息丢失问题 消息确认机制 生产者与MQ之间的消息确认&#xff1a; 当MQ成功接收消息后&#xff0c;会返回给生产者一个确认消息。如果在规定时间内生产者未收到确认消息&#xff0c;则任务消息发送失败。 MQ与消费者之间的消息确认&#xff1a; 当MQ成功接收消息后&#…

构建安全高效的前端权限控制系统

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

【C++】优先队列

优先队结构的不同物理结构与常用操作算法 优先队列是一种特殊的队列,队列中的元素具有优先级,每次弹出操作会弹出优先级最高的元素。 优先队列常用的物理结构有: 1. 数组:简单但不高效,插入和删除操作需要移动大量元素,时间复杂度高。 2. 二叉堆:是一种完全二叉树,通常用数…

新技术前沿-2024-大型语言模型LLM的本地化部署

参考快速入门LLM 参考究竟什么是神经网络 1 深度学习 1.1 神经网络和深度学习 神经网络是一种模拟人脑神经元工作方式的机器学习算法,也是深度学习算法的基本构成块。神经网络由多个相互连接的节点(也称为神经元或人工神经元)组成,这些节点被组织成层次结构。通过训练,…

Keil和VSCode协同开发STM32程序

系列文章 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 配置环境 2. 测试打开工程 3. 测试编译工程 随着项目的复杂度上升&#xff0c;开发者不仅需要强大的硬件支持&#xff0c;还需要一个高效和灵活的开发环境。 vscode是一款集成大量可以便携开发插件的代码…

Redis入门到通关之Redis数据结构-List篇

文章目录 ☃️概述☃️数据结构☃️源码☃️其他 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端…

FSRCNN:加速超分辨率卷积神经网络,SRCNN的加速版

paper&#xff1a;https://arxiv.org/pdf/1608.00367 code: https://github.com/yjn870/FSRCNN-pytorch/tree/master 目录 1. 动机 2. 方法 3. 代码对比 4. 实验结果 1. 动机 作者此前提出的SRCNN证明了CNN在图像超分领域的有效性。然而&#xff0c;SRCNN计算效率较低&#…

《Beginning C++20 From Novice to Professional》第五章 Arrays and Loops

循环和数组确实是联系比较紧密的两个基础语法&#xff0c;数组让我们管理大量同类对象&#xff0c;循环可以简单地遍历一个范围内的元素 本章我们可以学到&#xff1a; Arrays 数组开辟一段连续空间存储同类元素&#xff0c;我们通过【】下标来访问某个元素 如果无符号整型占…