公司网站建设费属于什么费用网站被黑了你会怎么想你该怎么做

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

公司网站建设费属于什么费用,网站被黑了你会怎么想你该怎么做,网站 建设 汇报,盐城代运营list的模拟实现 文章目录list的模拟实现一、list三个基本类的模拟实现总览二、节点类接口实现模拟实现构造函数三、迭代器类接口实现1.正向迭代器默认成员函数构造函数六种运算符重载 */-//–/!/2.反向迭代器四、list类接口实现1.默认成员函数1.1.构造函数1.2.析构函数1.3.…list的模拟实现 文章目录list的模拟实现一、list三个基本类的模拟实现总览二、节点类接口实现模拟实现构造函数三、迭代器类接口实现1.正向迭代器默认成员函数构造函数六种运算符重载 /-//–/!/2.反向迭代器四、list类接口实现1.默认成员函数1.1.构造函数1.2.析构函数1.3.拷贝构造函数1.4.赋值运算符重载函数2.访问相关函数2.1.front和back3.迭代器相关函数3.1.begin和end3.2.rbegin和rend4.插入删除相关函数4.1.insert4.2.push_back4.3.push_front4.4.erase4.5.pop_back4.6.pop_front5.其它相关函数5.1.resize5.2.clear5.3.empty_initialize5.4.swap一、list三个基本类的模拟实现总览 前面list的学习中我们得知list其实就是一个带头双向循环链表 现在要模拟实现list要实现下列三个类 模拟实现节点类模拟实现迭代器的类模拟list主要功能的类 这三个类的实现层层递进我们的目标是为了实现list类然而list的模拟实现其基本功能要建立在迭代器类和节点类均已实现好的情况下才得以完成。 namespace list_realize {//模拟实现list当中的结点类templateclass Tstruct List_node{//成员函数List_node(const T val T()); //构造函数//成员变量T _data; //数据域List_nodeT _next; //后继指针List_nodeT* _prev; //前驱指针};//模拟实现list正向迭代器templateclass T, class Ref, class Ptrstruct __list_iterator{typedef List_nodeT node;typedef list_iteratorT, Ref, Ptr Self;list_iterator(node* pnode); //构造函数//各种运算符重载函数Self operator();Self operator–();Self operator(int);Self operator–(int);bool operator(const Self it) const;bool operator!(const Self it) const;Ref operator();Ptr operator-();//成员变量node _pnode; //一个指向结点的指针};//模拟实现list反向迭代器templateclass Iterator, class Ref, class Ptrclass reverselist_iterator{typedef reverselist_iteratorIterator, Ref, Ptr Self;reverse__list_iterator(Iterator* it); //构造函数//各种运算符重载函数Self operator();Self operator–();Self operator(int);Self operator–(int);bool operator(const Self rit) const;bool operator!(const Self rit) const;Ref operator();Ptr operator-();//成员变量private:Iterator _it;//正向迭代器};//模拟实现listtemplateclass Tclass list{public:typedef List_nodeT node;typedef __list_iteratorT, T, T iterator;typedef list_iteratorT, const T, const T* const_iterator;typedef reverselist_iteratoriterator, T, T* reverse_iterator;typedef reverse__list_iteratorconst_iterator, const T, const T* const_reverse_iterator;//默认成员函数list(); // 无参构造template class InputIterator list(InputIterator first, InputIterator last); // 迭代器构造list(size_t n, const T val T()) // 带参构造list(const listT lt); // 拷贝构造listT operator(const listT lt); // 赋值运算符重载~list(); // 析构函数//迭代器相关函数iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;//访问容器相关函数T front();T back();const T front() const;const T back() const;//插入、删除函数void insert(iterator pos, const T val T());iterator erase(iterator pos);void push_back(const T x);void pop_back();void push_front(const T x);void pop_front();//其他函数void empty_initialize();size_t size() const;void resize(size_t n, const T val T());void clear();bool empty() const;void swap(listT lt);private:node* _head; //指向链表头节点的指针size_t _size; // 链表节点个数}; } 二、节点类接口实现 因为list的本质为带头双向循环链表所以其每个节点都要确保有下列成员 前驱指针后继指针data值存放数据 而节点类的内部只需要实现一个构造函数即可。 模拟实现 templateclass T struct List_node //因为成员函数都是公有就不设计成class了直接struct {List_nodeT* _next; // 前驱指针List_nodeT* _prev; // 后继指针T _data; // 记录存放数据List_node(const T val T()):_next(nullptr), _prev(nullptr), _data(val) {} };构造函数 构造函数这里是用来对其成员变量的一个初始化。 List_node(const T val T()):_next(nullptr), _prev(nullptr), _data(val) {}三、迭代器类接口实现 这里强调下因为list的特殊性其本质是带头双向循环链表对于链表我们已然得知其内存空间是不连续的是通过结点的指针顺次链接我们不能像先前的string和vector一样直接解引用去访问其数据结点的指针解引用还是结点结点指针还是结点指针归根结底在于list物理空间不连续。而string和vector的物理空间是连续的所以这俩不需要实现迭代器类可以直接使用。 为了能让list像vector一样去解引用访问到下一个数据我们需要单独写一个迭代器类的接口实现在其内部进行封装补齐相应的功能而这就要借助运算符重载来完成。 注意 迭代器又分为正向迭代器和反向迭代器。 1.正向迭代器 注意 这里我迭代器类的模板参数里面包含了3个参数 templateclass T, class Ref, class Ptr而后文list类的模拟实现中我对迭代器进行了两种typedef typedef __list_iteratorT, T, T* iterator;//普通迭代器 typedef __list_iteratorT, const T, const T* const_iterator;//const迭代器根据这里的对应关系Ref对应的是引用类型Ptr对应的是指针类型这里如果我是普通对象传过来的迭代器生成对应的普通迭代器如果是const对象传递过来的迭代器会生成对应的const迭代器。 这样做的原因在于避免单独写一个支持不能修改迭代器指向结点数据的类而造成的复用性差。 默认成员函数 这里的默认成员函数我们只需要写构造函数。 析构函数 – 结点不属于迭代器不需要迭代器释放拷贝构造 – 默认浅拷贝即可赋值重载 – 默认浅拷贝即可 构造函数 这里我们通过结点的指针即可完成构造。通过结点指针构造一个迭代器。 __list_iterator(node pnode)//通过结点指针构造一个迭代器:pnode(pnode) {}六种运算符重载 /-//–/!/ ** 运算符重载** *解引用的目的是为了获取结点里的 data数据因此我们直接return返回结点指向的_ data即可。 Ref operator()//结点出了作用域还在用引用返回 {return _pnode-_data;//返回结点指向的数据 }- 运算符重载 假设出现这样的场景我链表存储的不是内置类型而是自定义类型如下 struct AA {AA(int a1 0, int a2 0):_a1(a1), _a2(a2){}int _a1;int _a2; }; void test() {cpp::listAA 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)); }对于内置类型和自定义类型成员的指针其访问方式都是不同的 int it AA (it). 或者 it-而这里我们应该重载一个-运算符。以便于访问自定义类型成员的指针的数据。 //-运算符重载 Ptr operator-() { return (operator());//返回结点指针所指结点的数据的地址 //或者return _pnode-data; }实现了-运算符重载后我们执行it- a1编译器将其转换成it.operator-()此时获得的是结点位置的地址即AA*而理应还有一个箭头-才能获取数据也就是这样it.operator-()-_a1 总结编译器为了可读性进行优化处理如果不优化应该是it–_a1优化以后省略了一个箭头-。 运算符重载 运算符分为前置和后置 前置 迭代器的返回值还是迭代器这里的是为了让结点指向下一个结点的指针注意前置是要返回自增后的结点指针。 Self operator()//迭代器的返回值还是迭代器 {_node _pnode-_next;//直接让自己指向下一个结点即可实现return *this;//返回自增后的结点指针 }后置 为了区分前置后置通常要加上一个参数以便区别。此外后置是返回自增前的结点指针。 Self operator(int)//加参数以便于区分前置 {Self tmp(*this);//拷贝构造tmp_node _pnode-_next;//直接让自己指向下一个结点即可实现return tmp;//注意返回tmp才是后置 }– 运算符重载 –运算符也分前置–和后置– 前置– 前置–是让此结点指向上一个结点最后返回自减后的结点指针即可。 self operator–() {_pnode _pnode-_prev;//让_node指向上一个结点即可实现–return *this; }后置– 注意传参以区分前置–最后返回的是自减前的结点指针即可。 self operator–(int)//记得传缺省值以区分前置– {self tmp(*this);//拷贝构造tmp_pnode _pnode-_prev;return tmp; }! 运算符重载 这里比较是否不等是两个迭代器的比较直接返回两个结点的位置是否不同即可。 //!运算符重载 bool operator!(const self it) {return _pnode ! it._pnode;//返回两个结点指针的位置是否不同即可 }运算符重载 这里直接返回俩结点指针是否相同即可。 //运算符重载 bool operator(const self it) {return _pnode it._pnode;//返回俩结点指针是否相同 }2.反向迭代器 反向迭代器是一个适配器模式后文会将适配器。相较于正向迭代器反向迭代器有下面三种主要变化 反向迭代器的执行的操作是正向迭代器里的–反向迭代器里的–执行的操作是正向迭代器里的反向迭代器的解引用和-操作指向的是前一个数据 总代码如下 template class Iterator, class Ref, class Ptr class reverselist_iterator {typedef reverselist_iteratorIterator, Ref, Ptr Self; public:reverse__list_iterator(Iterator it):_it(it){}Ref operator(){Iterator prev _it;return –prev;}Ptr operator-(){return operator();}Self operator(){–_it;return *this;}Self operator–(){_it;return *this;}Self operator(int){_it–;return *this;}Self operator–(int){_it;return *this;}bool operator!(const Self rit) const{return _it ! rit._it;}bool operator(const Self rit) const{return _it rit._it;}private:Iterator _it; };四、list类接口实现 此接口的核心任务是为了模拟实现list类的一些核心功能好比如插入删除迭代器等等。 在list类中的唯一成员变量即自定义类型的变量由先前的结点类构成的头结点。 1.默认成员函数 1.1.构造函数 无参构造 此目的是为了对哨兵位的头结点_head进行初始化 list() {_head new Node();//申请一个头结点_head-_next _head;//头结点的下一个结点指向自己构成循环_head-_prev _head;//头结点的上一个结点指向自己构成循环_size 0; }传迭代器区间构造 先初始化再利用循环对迭代器区间的元素挨个尾插即可。 //传迭代器区间构造 template class InputIterator list(InputIterator first, InputIterator last) {empty_initialize();while (first ! last){push_back(*first);first;} }带参构造 初始化n个值为val list(size_t n, const T val T()) {empty_initialize();for (size_t i 0; i n; i){push_back(val);} }1.2.析构函数 这里可以先复用clear函数把除了头结点的所有结点给删除掉最后delete头结点即可。 ~list() {clear();delete _head;_head nullptr; }1.3.拷贝构造函数 假设要用lt1拷贝构造lt2。 传统写法 首先复用empty_init对头结点进行初始化接着遍历lt1的元素在遍历的过程中尾插将lt1的元素尾插到lt2上即可。这里直接利用push_back自动开辟空间完成深拷贝。 list(const listT lt) //list(const list lt) // 不建议不规范 {empty_initialize();for (const auto e : lt){push_back(e);} }现代写法 这里先初始化lt2自己再把lt1引用传参传给lt传lt的迭代器区间构造tmp复用swap交换头结点指针即可完成深拷贝的现代写法。 list(const listT lt) {empty_initialize(); // 空初始化listT tmp(lt.begin(), lt.end()); // 使用迭代器区间构造tmpswap(tmp); // 交换头结点指针 }1.4.赋值运算符重载函数 假设要把lt1赋值给lt2。 传统写法 listT operator(const listT lt) {if (this ! lt){clear();for (const auto e : lt){push_back(e);}}return *this; }现代写法 这里直接给出现代写法。注意这里传值传参把lt1传给lt自定义类型传值传参调用拷贝构造拷贝构造完成的是深拷贝生成了lt复用swap函数交换lt1与lt的头结点指针指向即可最后返回*this。 listT operator(listT lt)//套用传值传参去拷贝构造完成深拷贝 {swap(lt);return *this; }2.访问相关函数 2.1.front和back front和back函数分别用于获取第一个有效数据和最后一个有效数据因此实现front和back函数时直接返回第一个有效数据和最后一个有效数据的引用即可。 T front() {return *begin(); //返回第一个有效数据的引用// return _head-_next-_data; } T back() {return *(–end()); //返回最后一个有效数据的引用// return _head-_prev-_data; }我在这里 const T front() const {return begin();//return _head-_next-_data; }const T back() const {return _head-_prev-_val; }3.迭代器相关函数 3.1.begin和end begin的作用是返回第一个位置的结点的迭代器而第一个结点就是哨兵位头结点的下一个结点因此直接return返回_head的_next即可。end的作用就是返回最后一个有效数据的下一个位置的迭代器而这里对于list指的就是头结点_head的位置。 begin和end均分为普通对象调用和const对象调用因此要写两个版本。 普通对象调用版 iterator begin()//begin返回的就是第一个有效数据即头结点的下一个结点 {return iterator(_head-_next);//构造了一个匿名对象通过调用构造函数利用头结点指向的第一个结点作为参数来返回头结点//return _head-_next; 也可以这样写 }iterator end() {return iterator(_head);//end返回的是最后一个结点的下一个结点就是头结点_head//return _head; 也可以这样写 }const对象调用版 const_iterator begin() const {return const_iterator(_head-_next);//return _head-_next; } const_iterator end() const {return const_iterator(_head);//return _head; 也可以这样写 }3.2.rbegin和rend rbegin就是正向迭代器里end()的位置rend就是正向迭代器里begin()的位置。 rbegin和rend同样分为普通对象调用和const对象调用 普通对象调用 reverse_iterator rbegin() {return reverse_iterator(end()); }reverse_iterator rend() {return reverse_iterator(begin()); }const对象调用 const_reverse_iterator rbegin() const {return const_reverse_iterator(end()); }const_reverse_iterator rend() const {return const_reverse_iterator(begin()); }4.插入删除相关函数 4.1.insert 实现insert首先创建一个新的结点存储插入的值接着取出插入位置pos的结点为cur记录cur的上一个结点位置prev先链接prev和newnode再链接newnode和cur即可。最后记得要返回新插入元素的迭代器位置。 //insert插入pos位置之前
iterator insert(iterator pos, const T val) {node
newnode new node(val); //创建新的结点node* cur pos._pnode; //迭代器pos处的结点指针//链接prev和newnodenode* prev cur-_prev;prev-_next newnode;newnode-_prev prev;//链接newnode和curnewnode-_next cur;cur-_prev newnode;_size;//返回新插入元素的迭代器位置return iterator(newnode); } 补充list的insert不存在野指针和意义改变的迭代器失效问题。 4.2.push_back 法一 首先创建一个新结点用来存储尾插的值接着找到尾结点。将尾结点和新结点前后链接构成循环再将头结点和新结点前后链接构成循环即可。 void push_back(const T x) {Node* tail _head-_prev;//找尾Node* newnode new Node(x);//创建一个新的结点//链接tail和newnodetail-_next newnode;newnode-_prev tail;//链接newnode和头结点_headnewnode-_next _head;_head-_prev newnode; }法二 这里也可以复用insert函数当insert中的pos位置为哨兵位头结点的位置时实现的就是尾插因为insert插入是在pos位置前插入而pos位哨兵位头结点时在其前一个位置尾部插入就是实现了尾插。 void push_back(const T x) {//法二复用insertinsert(end(), x); }4.3.push_front 直接复用insert函数当pos位置为begin()时获得的pos就是第一个有效结点数据即可满足头插。 void push_front(const T x) {insert(begin(), x); }4.4.erase erase删除的是pos位置的结点首先取出pos位置的结点为cur记录cur上一个结点的位置为prev再记录cur下一个结点的位置为next链接prev和next最后delete释放cur的结点指针即可。最后记得返回删除元素后一个元素的迭代器位置。 iterator erase(iterator pos) {assert(pos ! end())node* cur pos._pnode;node* prev cur-_prev;node* next cur-_next;//链接prev和nextprev-_next next;next-_prev prev;delete cur;cur nullptr;_size–;return iterator(next);//返回删除元素下一个元素的迭代器位置}erase存在迭代器失效问题为了避免此问题我们修改了返回值为iterator。 4.5.pop_back 直接复用erase即可当pos位置为–end()时pos就是最后一个结点的位置实现的就是尾删。 void pop_back() {erase(–end()); }4.6.pop_front 直接复用erase即可当pos位置为begin()时pos就是第一个有效数据实现的就是头删。 void pop_front() {erase(begin()); }5.其它相关函数 5.1.resize resize函数的规则 若当前容器的size小于所给n则尾插结点直到size等于n为止。若当前容器的size大于所给n则只保留前n个有效数据。 实现resize函数时不要直接调用size函数获取当前容器的有效数据个数因为当你调用size函数后就已经遍历了一次容器了而如果结果是size大于n那么还需要遍历容器找到第n个有效结点并释放之后的结点。 这里实现resize的方法是设置一个变量oldsize用于记录当前所遍历的数据个数然后开始变量容器在遍历过程中 当oldsize大于或是等于n时遍历结束此时说明该结点后的结点都应该被释放将之后的结点释放即可。 当容器遍历完毕时遍历结束此时说明容器当中的有效数据个数小于n则需要尾插结点直到容器当中的有效数据个数为n时停止尾插即可。 void resize(size_t newsize, const T val T()) {size_t oldsize size();if (newsize oldsize){// 有效元素个数减少到newsizewhile (newsize oldsize){pop_back();oldsize–;}}else{while (oldsize newsize){push_back(val);oldsize;}} }5.2.clear clear的作用是清除除了头结点外的所有结点这里可以复用erase并通过遍历的方式逐个删除。 void clear() {//复用eraseiterator it begin();while (it ! end()){it erase(it);//用it接收删除后的下一个结点的位置}
}5.3.empty_initialize 由于对头结点初始化使用频繁我们特定把它封装成一个函数作用就是把哨兵位的头结点开辟出来。 //空初始化 对头结点进行初始化 void empty_initialize() {_head new Node();_head-_next _head;_head-_prev _head;_size 0; }5.4.swap 对于链表的swap直接交换头结点指针的指向即可完成。直接复用库函数的swap即可。 void swap(listT lt) {std::swap(_head, lt._head);std::swap(_size, lt._size); }