如何利用百度网盘搭建视频网站并集成Wordpress和微信openid?

摘要:用百度网盘做视频网站,Wordpress 微信 openid,wordpress采集站源码,公司网站建站要多少钱一年list 模拟实现回顾准备构造析构函数的构造构造方法析构方法赋值运算符重载容量相关接口元素获取元素修改相关接口push 、p
用百度网盘做视频网站,Wordpress 微信 openid,wordpress采集站源码,公司网站建站要多少钱一年list 模拟实现回顾准备构造析构函数的构造构造方法析构方法赋值运算符重载容量相关接口元素获取元素修改相关接口push 、popinserterase清空交换迭代器 **#xff08;重点#xff09;迭代器基本概念迭代器模拟实现回顾 在上一篇博客中我们大致了解了 list 相关接口的使用方法… list 模拟实现回顾准备构造析构函数的构造构造方法析构方法赋值运算符重载容量相关接口元素获取元素修改相关接口push 、popinserterase清空交换迭代器 **重点迭代器基本概念迭代器模拟实现回顾 在上一篇博客中我们大致了解了 list 相关接口的使用方法并进行了一系列的测试练习那么这一小节就来模拟实现一些接口吧~ list 是一个带头双向循环的链表 在模拟实现这些接口之前我们需要先进行节点信息的创建 templateclass T //采用模板------可以定义不同数据类型的 list 链表struct ListNode {ListNode(const Tvalue T()) //构造方法:prev(nullptr), next(nullptr), val(value){}ListNode* prev; //前驱节点ListNode* next; //后继节点T val; //值域};准备 为了与类中 list 进行区分我们可以自定义一个命名空间在该命名空间内部来进行接口的模拟实现 namespace xx {templateclass Tstruct ListNode //创建节点信息{ListNode(const T value T()):prev(nullptr), next(nullptr), val(value){}ListNode* prev;ListNode* next;T val;}; }为了便于测试我们在自己的命名空间中定义一个打印信息的函数 templateclass Tvoid PrintList(const listT L){for (auto e : L)cout e ;cout endl;}定义链表信息 //定义链表信息templateclass Tclass List {typedef ListNodeT Node; //取别名public://构造方法//析构方法//接口模拟实现private:Node* _head; //头节点};构造析构函数的构造 构造方法 由于 list 链表是一个带头的双向循环链表并且已知 _head 头节点因此在创建链表时候要注意 prev 与 next 的指向 创建任何类型的构造方法之前我们首先要定义出一个带头结点的空链表 void CreateList(){//创建头节点_head new Node();_head-next _head-prev _head; //构造循环}1构造空链表 创建一个空链表也就是创建一个只有头节点的链表因此我们可以直接在构造方法内部调用头节点创建的函数即可 List() {CreateList();}2构造具有 n 个值为 val 的链表 构造一个具有 n 个相同节点的链表我们可以复用尾插或头插都可以来进行 List(int n, const T val T()){CreateList(); //首先创建头节点信息//构造 n 个值为 val 的节点我们可以采用 n 次尾插来进行for (int i 0; i n; i) {push_back(val);}}注意 3区间构造 templateclass IteratorList(Iterator first, Iterator last){CreateList(); //创建头节点auto it first;while (it ! last) {push_back(*it); //同样采用多次尾插方法来构造*it 代表获取节点值域it; //迭代器 it 的自增------表示获取下一个节点的位置}} 注意 在进行参数构造时候我们参数类型定义均为模板类型 Iterator 因此对于上述构造 n 个值相同的节点信息的链表倘若我们使用 size_t 类型来传入 n 参数会使得 编译器在进行类型推演时候默认为两个变量类型不一致导致编译器调用错误这也就解释了为什么将 n 变量类型定义为 int 的原因。 4拷贝构造 用一个已有的链表来创建新的链表信息 List(const ListT L){CreateList(); //创建头节点for (auto e : L) {push_back(e); //遍历 L 链表同时将遍历到的节点值插入到 新构造的链表中}}析构方法 ~List(){clear(); //清空所有节点信息delete _head; //删除头节点_head nullptr;}赋值运算符重载 我们采用现代版的写法传递的参数为值类型的参数会调用一次拷贝构造函数来构造出该参数然后将该参数与 this 指针指向的内容进行交换即可可以参考深浅拷贝 添加链接描述 ListT operator(const ListT L) //以值的方式传参会调用一次拷贝构造方法来创建参数具有自己独立的地址空间并在函数调用结束自动销毁临时空间{this-swap(L); //交换之后L 中空间改变为原来的 this 空间并在函数调用结束自动进行了析构销毁return *this; }容量相关接口 由于 list 链表结构为带头双向循环链表我们只知道 头节点 _head 的信息因此在进行容量判断时候需要对整个链表进行遍历------注意遍历条件 1size 接口 size_t size()const{//统计节点个数int count 0;Node* cur _head-next;while (cur ! _head) { //循环链表的 next 指针域一定是不为空的因此遍历条件应该是判断是否回到头节点位置count;cur cur-next;}return count;}2判空 循环链表为空时也就是只有一个头节点存在故判断条件应为 bool empty()const{return _head _head-next;}3resize 修改有效节点的个数 当缩小有效节点个数为 newsize 时我们需要将 newsize 之后的节点进行删除 当扩大有效节点个数为 newsize 时我们需要在原有的节点尾部插入 newsize-oldsize 个新的节点且新节点的值为 val void resize(size_t newsize,const T valT()){size_t oldsize size(); //统计现有的节点个数//当缩小节点if (newsize oldsize) {for (int i newsize; i oldsize; i)pop_back(); //进行尾删}else {//增大节点个数for (int i oldsize; i newsize; i)push_back(val); //进行尾插操作}} 元素获取 1获取首节点信息 //非 const 类型表示可以对节点信息进行修改操作T front(){//获取首节点值return _head-next-val; //_head 为头节点它所存储的数据不是有效数据}const T front()const //只读{//获取首节点值return _head-next-val; //_head 为头节点它所存储的数据不是有效数据}2获取尾节点信息 T back(){return _head-prev-val; //_head 为头节点它的前驱节点为尾节点}const T back()const{return _head-prev-val; //_head 为头节点它的前驱节点为尾节点} 元素修改相关接口 push 、pop 由于头部或尾部插入新元素都可以直接复用 insert 任意位置插入方法因此我们这里直接调用 insert 接口来实现 void push_front(const T val){insert(begin(), val); //begin() 指向首节点因此进行头插 直接在第一个节点前插入新节点 }void push_back(const T val){insert(end(), val); //end() 指向头节点因此进行尾插直接在 end() 之前插入新节点}由于头部或尾部删除之间可以调用 erase 任意位置删除所有这里也直接调用erase 接口 void pop_front(){if (empty())return; //链表为空不能进行删除//头删------删除首节点erase(begin()); //删除 begin() 位置节点}void pop_back(){if (empty())return; //链表为空不能进行删除//尾删------删除最后一个节点erase(end()); }注意看这两段代码是否有问题 能这么问当然是有问题啦~ 具体什么问题我们来看看 仔细观察 begin() 与 end() 的位置我们发现 begin() 指向的就是第一个节点的位置因此进行头删时候直接可以进行删除并且删除之后并不会影响之后元素的访问 而 end() 指向的是头节点的位置而我们要删除的是最后一个有效节点也就是 end() 的前一个节点位置因此此处的尾删函数的实现是不对的具体修改如下 void pop_back(){if (empty())return; //链表为空不能进行删除//尾删------删除最后一个节点auto pos end();--pos; //迭代器向前移动到尾节点位置erase(pos); //删除 end() 前一个位置节点}list 接口测试中我们谈到 在插入节点时不会导致迭代器的失效而在删除元素时候会引发迭代器失效但是不会导致迭代器位置之后的元素的访问因此一般在删除节点操作之后我们会接收返回值的信息来防止迭代器失效。 list 接口使用中我们已经进行了测试忘记的宝子参考添加链接描述 insert 在任意位置进行元素的插入首先我们需要创建出一个新节点信息然后对节点的指向进行修改即可 Iterator insert(Iterator pos, const T val){//在给定的节点位置 pos 之前进行新节点的插入Node* newnode new Node(); //创建新节点newnode-val val;newnode-next pos;newnode-prev pos-prev;pos-prev-next newnode;pos-prev newnode;return newnode; //返回新插入的节点位置} 画个图来理解一些吧~ erase Iterator erase(Iterator pos){//删除给定的 pos 位置的节点if (pos _head)return _head; //头节点不能进行删除Node* cur pos-next; //记录下一个节点位置//修改指向cur-prev pos-prev;pos-prev-next cur;delete pos;return cur; //返回删除节点的位置-------此时迭代器 pos 已经失效了}删除节点与插入节点过程很类似注意修改指向的顺序读者可以自己画画图来理解 清空 clear 是将 list 链表中所有节点进行删除我们可以采用头删的方法来进行 void clear(){Node* cur _head-next; //进行头删while (cur ! _head) {cur-next-prev _head;_head-next cur-next;delete cur;cur _head-next; //修改要删除的节点位置}_head-next _head-prev _head; //最后删除头节点}交换 void swap(ListT L) {std::swap(_head, L._head); //之间采用全局 swap 函数交换头节点位置即可} 迭代器 **重点 迭代器基本概念 string 、vector 还有现在学习的 list 当中我们都有使用到迭代器那么迭代器到底是什么呢 在前边的学习中我们提到 迭代器可以看作是原生态的指针类型在模拟接口中我们发现定义的迭代器变量我们可以对其进行以下操作 1解引用 * 2自增自减 3迭代器的比较 例如在遍历时我们使用到的迭代器vector 容器下的迭代器 因此我们在模拟实现迭代器时候也要能够进行这三种基本操作。 接下来我们来模拟实现一些迭代器吧~ 迭代器模拟实现 在前边模拟实现 string 以及 vector 时我们将迭代器处理为原生态的指针类型发现在整个模拟接口测试过程中是没有任何问题的说明在之前的模拟实现中 迭代器就是被当作指针来进行处理的那么在 list 中我们是否也可以这么处理 同样将迭代器看作是原生态的指针类型 typedef Node* Iterator;当我们进行迭代器的解引用以及自增自减 auto it begin();while (it!end()){cout *it ;it;}cout endl;那么为什么在 string 和 vector 当中迭代器可以被处理成为 原生态的指针并且可以正常使用在这里就不行了呢大家可以思考思考 解答 回顾我们在学习 string 和 vector 容器中使用的是顺序结构也就是说所采用的容器空间是连续的因此进行自增自减可以直接获取到它相邻的前后元素位置 而在 list 中我们知道 list 是多个节点构成的链表而每一个节点都是在使用时才创建new出来的然后将创建的新节点链接到我们的链表当中由此可见 list 当中的结构并不一定连续的故不能直接进行迭代器的自增自减操作 其次假如将迭代器定义为原生态的指针类型 Node* 在进行解引用操作时候取到的类型是 Node 而并非是当前迭代器指向位置的值域val信息因此迭代器不能被简单的处理为 原生态的指针 再者我们提到迭代器可以进行比较而原生态指针类型定义出来的迭代器类型都是一致的类型间如何进行比较 由此可见在 list 中模拟实现迭代器时我们首先需要对迭代器进行封装操作 正向迭代器的封装 //封装迭代器类-------------要求能够使用指针解引用并能够进行自增自减操作并能够进行迭代器的比较templateclass T,class Ptr,class Refclass ListIterator {typedef ListNodeT Node;public:typedef Ptr Ptr; //类型重命名指明当前 Ptr 是类型而非变量typedef Ref Ref;typedef ListIteratorT, Ptr, Ref Self; //因为节点当中包含值域和指针类型因此我们设计迭代器时需要能够返回不同的类型的数据信息public:ListIterator(Node* pNode nullptr) :_pNode(pNode){}// 解引用Ref operator*() //返回值信息应该是节点当中的数据类型{return _pNode-val; //解引用也就是获取当前节点中的值域信息}Ptr operator-() //返回值信息为当前节点数据域的地址{return (_pNode-val); //在自定义类型中体现很明显}/// 自增自减Self operator() //返回值为迭代器自身类型{_pNode _pNode-next; //自增操作也就是将迭代器的位置向后移动---------获取下一个位置的节点return *this;}Self operator(int) //后置{Self tmp(*this); //后置先使用后对其进行修改_pNode _pNode-next;return tmp;}Self operator--(){_pNode _pNode-prev; //自减操作也就是将迭代器向前移动--------获取前一个位置的节点return *this; }Self operator--(int) //后置--{Self tmp(*this);_pNode _pNode-prev;return tmp;}迭代器能够比较bool operator!(const Self s)const{return _pNode ! s._pNode;}bool operator(const Self s)const{return _pNode s._pNode;}Node* _pNode;}; 反向迭代器的封装 在前边我们介绍到正向迭代器 begin() 的位置与反向迭代器 rend() 位置相同正向迭代器 end() 位置与反向迭代器 rbegin() 位置相同两种迭代器正好相反因此进行反向迭代器封装我们可以复用正向迭代器的方法 templateclass Iteratorstruct ListReverseIterator {//typename 是为了说明 Ref Ptr 是属于正向迭代器 Iterator 中的类型而不是静态成员变量typename typedef Iterator::Ref Ref;typename typedef Iterator::Ptr Ptr;typedef ListReverseIteratorIterator Self;public:ListReverseIterator(Iterator it) :_it(it){}Ref operator*(){Iterator tmp _it; //rbegin() 指向 _head 头节点不需要进行打印因此 解引用 获取到的是第一个节点的值域即需要将 rbegin 向后操作移动-----------即需要将正向迭代器的 end() 向前移动--操作--tmp;return *tmp; //返回值是节点数据域中的数据类型}Ptr operator-() //获取当前节点数据域的地址信息{return (_it-pNode-val);}Self operator() {--_it; //反向迭代器的前置 等价于 正向迭代器的前置--return *this;}Self operator(int){_it--; //反向迭代器后置 等价于 正向迭代器的后置--return *this; }Self operator--() {_it; //反向迭代器的前置-- 等价于 正向迭代器的前置return *this;}Self operator--(int){_it; //反向迭代器后置-- 等价于 正向迭代器的后置return *this;}///bool operator!(const Self s)const{return _it ! s._it;}bool operator(const Self s)const{return _it s._it;}Iterator _it;};封装之后我们需要在我们自己定义的 List 类中进行声明 typedef ListIteratorT,T*,T Iterator; //迭代器封装之后要能够返回不同的数据类型节点值域节点的地址等typedef ListIteratorT, const T*, const T const_Iterator; //const 迭代器-----只读typedef ListReverseIteratorIterator reverse_iterator;typedef ListReverseIteratorconst_Iterator const_reverse_iterator; //const 迭代器-----只读则迭代器的模拟接口实现如下 Iterator begin() //begin() 获取首节点的位置{return Iterator(_head-next); //返回值类型的临时对象}Iterator end(){return Iterator(_head);}//反向迭代器reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}//const 迭代器const_Iterator begin()const{return const_Iterator(_head-next);}const_Iterator end()const{return const_Iterator(_head);}const_reverse_iterator rbegin()const{return const_reverse_iterator(end());}const_reverse_iterator rend()const{return const_reverse_iterator(begin());}由于我们对迭代器进行了封装而并非原始方式定义为原生态的指针类型因此采用迭代器来进行插入删除元素的函数也要进行相应的修改操作 Iterator insert(Iterator Itpos, const T val){//在 pos 位置之前进行插入Node* pos Itpos._pNode; //此时的迭代器 Itpos 所指向的并不是当前节点的位置信息而是包含三种数据结构的信息因此需要进行取节点操作//后续的代码与前边是相同的Node* newnode new Node();newnode-val val;newnode-prev pos-prev;newnode-next pos;pos-prev-next newnode;pos-prev newnode;return newnode;}Iterator erase(Iterator Itpos){//删除 pos 位置的元素Node* pos Itpos._pNode;//此时的迭代器 Itpos 所指向的并不是当前节点的位置信息因此需要进行取节点操作//后续的代码与前边是相同if (pos _head)return pos;Node* cur pos-next;pos-prev-next cur;cur-prev pos-prev;delete pos;return cur;}好了今天的学习就到这里啦 对于迭代器部分的理解可能比较困难读者可以自己在代码当中多调试调试看看 关于本节具体的代码实现请参考mylist 文件添加链接描述 有任何问题欢迎评论留言哦