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://www.hqwc.cn/news/643810.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32知识记录

文章目录 单片机发送AT指令给ESP8266接收手机app数据的结构体C语言的枚举类枚举类的应用 设置水泵开启关闭代码分析DS18B20的端口项目接线问题ADC全部信道STM32F103C8T6引脚定义 单片机发送AT指令给ESP8266 以下是一个简单的示例&#xff0c;演示了如何使用AT指令从单片机发送…

【CouchDB 与 PouchDB】

CouchDB是什么 CouchDB&#xff0c;全名为Apache CouchDB&#xff0c;是一个开源的NoSQL数据库&#xff0c;由Apache软件基金会管理。CouchDB的主要特点是使用JSON作为存储格式&#xff0c;使用JavaScript作为查询语言&#xff08;通过MapReduce函数&#xff09;&#xff0c;并…

vuetify3.0+tailwindcss+vite最新框架

1、根据vuetify官网下载项目 安装vuetify项目 2、根据tailwindcss官网添加依赖 添加tailwindcss依赖 3、 配置main.ts // main.ts import "./style.css"4、使用 <template><h1 class"text-3xl font-bold underline">Hello world!</…

MySQL无法打开情况下读取frm文件的表结构

一、背景&#xff1a; 开发人员通过MySQL客户端工具&#xff0c;可以访问MySQL5.7.6&#xff0c;可以访问具体的DB&#xff0c;可以查看小写表的数据&#xff0c;但是无法查看大写表的数据&#xff0c;报错信息为“table does not exist”。 二、检查与分析&#xff1a; ssh登录…

virtualbox 网络设置实现主机和虚拟机互相访问

前言 一般来说&#xff0c;virtualbox 虚拟机的上网模式是 NAT。这样虚拟机可以上网并访问宿主机&#xff0c;但宿主机无法访问虚拟机&#xff0c;也无法 ping 通。下面介绍双网卡模式&#xff0c;实现虚拟机和宿主机能够互相访问 ping 通。 双网卡模式 进入虚拟机的网络设置…

快速新建springboot项目

一、初始化 1.打开IDEA&#xff0c;在Spring initializer这里按照下图项目进行配置。注意&#xff1a;如果jdk是1.8建议将Server URL这里替换为图中的阿里云服务器&#xff0c;否则容易找不到对应的java8&#xff0c;然后点击next 2.在这里提前配置一些需要使用的依赖&#xf…

数据分析专家能力模型

招式&#xff1a;懂商业&#xff08;业务能力&#xff09; 外功更偏重于技能&#xff0c;首先需要懂招式&#xff0c;即懂商业&#xff0c;数据分析最终是为业务服务的&#xff0c;无论是互联网企业准求的用户增长和UJM分解&#xff0c;还是传统企业追求的降本增效和精细化运营…

维基百科、百度百科和搜狗百科词条的创建流程

随着网络的发展&#xff0c;百度百科、搜狗百科、维基百科等百科网站已经成为大众获取知识的重要途径。因为百科具有得天独厚的平台优势&#xff0c;百科上的信息可信度高&#xff0c;权威性强。所以百科平台也成为商家的必争之地。这里小马识途聊聊如何创建百度百科、搜狗百科…

Linux中的vi与vim:编辑器的王者之争与深度探索

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、Linux的起源与发展 2、vi与vim的历史与发展 …

mac电脑搭建vue环境(上篇)

第一步&#xff1a;mac电脑要有homebrew&#xff0c;如何安装homebrew 点击下方 MAC安装homebrew-CSDN博客 第二步&#xff1a;homebrew安装node.js 第三步&#xff1a;安装npm 第四步&#xff1a;安装webpack 第五步&#xff1a;安装vue脚手架 第六步&#xff1a;可以在…

小扎宣布开放 Meta Horizo​​n OS

日前&#xff0c;Meta以“混合现实的新时代”为题的博文宣布向第三方制造商开放Meta Horizon OS&#xff0c;包括华硕、联想和微软Xbox等等&#xff1a; Meta正在朝着为元宇宙建立一个更开放的计算平台的愿景迈出下一步。Meta正在向第三方硬件制造商开放赋能Meta Quest设备的操…

贪心算法在单位时间任务调度问题中的应用

贪心算法在单位时间任务调度问题中的应用 一、引言二、问题描述与算法设计三、算法证明四、算法实现与效率分析五、C语言实现示例六、结论 一、引言 单位时间任务调度问题是一类经典的优化问题&#xff0c;旨在分配任务到不同的时间槽中&#xff0c;使得某种性能指标达到最优。…