广州模板建站多少钱网站建设亇金手指下拉排名亅

当前位置: 首页 > news >正文

广州模板建站多少钱,网站建设亇金手指下拉排名亅,物理服务器,vps 一个ip 多个网站 软件 linux文章目录 前言一、结点类的实现二、迭代器类的实现迭代器类的存在意义迭代器类的模板参数构造函数运算符的重载–运算符的重载、!运算符的重载*运算符的重载-运算符的重载 总结 前言 注意本篇难度偏高#xff0c;其主要体现在迭代器类的实现#xff01;   什么#xf… 文章目录 前言一、结点类的实现二、迭代器类的实现迭代器类的存在意义迭代器类的模板参数构造函数运算符的重载–运算符的重载、!运算符的重载运算符的重载-运算符的重载 总结 前言 注意本篇难度偏高其主要体现在迭代器类的实现   什么list类的迭代器还要单独封装成类 还真是毕竟它的元素存储在物理意义上不是连续的   正文开始 一、结点类的实现 但是首先我们得先来实现一下节点类因为我们说list底层是个链表其实更准确地说是双向链表 而实现一个结点类。而一个结点需要存储的信息有数据、前一个结点的地址、后一个结点的地址于是该结点类的成员变量也就出来了数据、前驱指针、后继指针 而对于该结点类的成员函数来说我们只需实现一个构造函数即可。因为该结点类只需要根据数据来构造一个结点即可而结点的释放则由list的析构函数来完成 // List的节点类 // 直接设置为公开访问即可后面很明显有访问成员变量的必要templateclass Tstruct ListNode{// 若构造结点时未传入数据则默认以list容器所存储类型的默认构造函数所构造出来的值为传入数据// 对内置类型和自定义类型都是如此ListNode(const T val T()): _prev(nullptr), _next(nullptr), _val(val){}ListNodeT _prev;ListNodeT* _next;T _val;};二、迭代器类的实现 来了来了 这是我们要实现的三个类而迭代器类的实现基础就是我们刚才实现的节点类   你可能注意到我们是把迭代器类给公开的数据公开意味着内部数据可以被任意修改。但是在这里没人会去跳过封装使用内部的数据没有意义。因为不同编译器中底层实现是不一样的(实现逻辑、名称)这本身就是一种隐式设置为私有的作用 迭代器类的存在意义 之前模拟实现string和vector时都没有说要实现一个迭代器类为什么实现list的时候就需要实现一个迭代器类了呢其实就像前言说的因为string和vector对象都将其数据存储在了一块连续的内存空间我们通过指针进行自增、自减以及解引用等操作就可以对相应位置的数据进行一系列操作因此string和vector当中的迭代器就是原生指针typedef一下就行 但是对于list来说其各个结点在内存当中的位置是随机的并不是连续的我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作因为链表的各个元素只在逻辑上连续 而迭代器的意义就是让使用者可以不必关心容器的底层实现可以用简单统一的方式对容器内的数据进行访问我们也想有个list迭代器可以简单的、- -、理解成本低所以封装节点指针重载运算符就是我们要做的工作 比如说我们重载的时候其实就是在内部让p p - _next只是运用的时候没必要知道而已 迭代器类的模板参数 templateclass T, class Ref, class Ptr你可能会感到诧异为什么迭代器类会有三个模板参数其实迭代器分为两种普通迭代器和const迭代器我们在list类的模拟实现中会运用到这两个 typedef ListIteratorT, T, T iterator;typedef ListIteratorT, const T, const T const_iterator;这体现了一种封装智慧因为两种迭代器的自增自减等等几乎没区别区别就仅仅在于元素的访问和获取指向元素的指针这两个成员函数的返回值上比如说元素的访问一个是T一个是const T如果是仅仅因为这点小区别我们就写两个类那这代码就不算好于是我们选择丢给编译器让它实例化的时候帮我们自动生成尽管这也是实现两个类但是我们少写了一个 好风凭借力请说谢谢编译器先生 还是那句话没有什么岁月静好都是有人在帮你负重前行 所以Ref和Ptr分别代表的其实是引用类型和指针类型 构造函数 迭代器类实际上就是对结点指针进行了封装其成员变量就只有一个那就是结点指针其构造函数直接根据所给结点指针构造一个迭代器对象即可 ListIterator(Node* node nullptr): _node(node){}运算符的重载 前置原本的作用是将数据自增然后返回自增后的数据。我们的目的是让结点指针的行为看起来更像普通指针那么对于结点指针的前置我们就应该先让结点指针指向后一个结点然后再返回“自增”后的结点指针即可 而对于后置我们则应该先记录当前结点指针的指向然后让结点指针指向后一个结点最后返回“自增”前的结点指针即可 Self operator(){_node _node-_next;return *this;}//有拷贝构造就需要考虑深浅拷贝的问题。//这里需要使用到浅拷贝指向同一块空间并且不需要考虑重复析构的问题也说明了浅拷贝并都是坏处。//临时对象tmp同指向一块空间调用完临时对象被销毁指向空间资源保留//这也导致了返回类型是指针还是引用Self operator(int){Self temp(*this);_node _node-_next;return temp;}typedef其实可以将一个较长的类型变短这里的Self其实就是迭代器类自己 typedef ListIterator class T, class Ref, class Ptr Self; –运算符的重载 对于前置- -我们应该先让结点指针指向前一个结点然后再返回“自减”后的结点指针即可 而对于后置- -我们则应该先记录当前结点指针的指向然后让结点指针指向前一个结点最后返回“自减”前的结点指针即可 Self operator–(){_node _node-_prev;return *this;}Self operator–(int){Self temp(*this);_node _node-_prev;return temp;}、!运算符的重载 当使用、!运算符比较两个迭代器时我们实际上想知道的是这两个迭代器是否是同一个位置的迭代器也就是说我们判断这两个迭代器当中的结点指针的指向是否相同即可 // 迭代器间的比较并不是比较指向数据而是比较迭代器指向的位置bool operator!(const Self it) const{ return _node it._node;}bool operator(const Self it) const{ return _node ! it._node;}运算符的重载 当我们使用解引用操作符时是想得到该位置的数据内容。因此我们直接返回当前结点指针所指结点的数据即可但是这里需要使用引用返回因为解引用后可能需要对数据进行修改 Ref operator() { return _node-_val;}-运算符的重载 我们使用迭代器的时候可能会用到-运算符一般发生在T也是自定义类型的时候 int main() {listDate lt;Date d1(2021, 8, 10);Date d2(1980, 4, 3);Date d3(1931, 6, 29);lt.push_back(d1);lt.push_back(d2);lt.push_back(d3);listDate::iterator pos lt.begin();cout pos-_year endl; //输出第一个日期的年份此时Date的成员变量为公有return 0; }这个时候我们只需要返回节点所存储数据的指针即可 Ptr operator-() { return (operator*()); // 复用毕竟就是拿元素又是取地址符}可是你可能会感觉不对按道理来说这种情况不是应该两个-吗怎么pos-_year就拿到年份这一变量了 其实这还是编译器弄的鬼因为假如一个地方出现两个箭头程序的可读性太差了所以编译器做了特殊识别处理为了增加程序的可读性省略了一个箭头 所以说pos-_year其实本质上是pos–_year更准确的说是pos.operator-()-_year 第一个箭头是pos -去调用重载的operator-返回Date 的指针第二个箭头是Date* 的指针去访问对象当中的成员变量_year 那能不能自己显式写出两个-呢你自己试试pos–_year不行pos.operator-()-_year可以 总结 本篇暂时就先介绍两个类剩下最后一个list类我们下篇再介绍   铠甲要合体了