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

重庆建站模板百度号码

重庆建站模板,百度号码,做哪类网站,免费海报制作前言 数据结构中,我们了解到了链表,但是我们使用时需要自己去实现链表才能用,但是C出现了list将这一切皆变为现。list可以看作是一个带头双向循环的链表结构,并且可以在任意的正确范围内进行增删查改数据的容器。list容器一样也是…

前言

数据结构中,我们了解到了链表,但是我们使用时需要自己去实现链表才能用,但是C++出现了list将这一切皆变为现。list可以看作是一个带头双向循环的链表结构,并且可以在任意的正确范围内进行增删查改数据的容器。list容器一样也是不支持下标访问的,主要也是物理空间并不连续。

火车侧面图 的图像结果

list的部分重要接口实现

一样的,在实现之前,我们要对list的结构有一个大概的框架

 在此基础就很容易理解list的内部是一个个节点,该节点的类型也都是自定义类型,而模版参数是节点中val的类型。代码框架如下:

namespace cr
{template<class T>struct ListNode{ListNode(const T& x = T())//T()匿名对象:val(x),pre(nullptr),next(nullptr){}T val;struct ListNode<T>* pre;struct ListNode<T>* next;};template<class T>class list{typedef ListNode<T> Node;//对类进行重命名public:private:Node* _head;//自定义类型成员的指针};}

1.迭代器实现

cr::list<int>::iterator it = lt.begin();
while (it != lt.end())//lt.end()临时对象
{cout << *it << endl;it++;
}

先看这段代码,该代码也就是我们想要最终实现的结果,所以我们想知道对迭代器进行哪些操作,可以先从需要实现的功能进行分析。可以初步知道迭代器“八成”是节点的指针(节点是自定义类型),所以*it和it++这两个操作铁定是需要进行运算符重载的,*it实际就是得到节点类的成员变量val的值,而it++就是通过指针的偏移,指向下一个节点。而最重要的是应该知道*it和it++操作对象是迭代器而不是list实例化出来的对象(lt),像begin和end函数面向的就是list实例化的对象,所以可以直接在list类中实现。如果*it和it++也在在list类中实现的话,默认传参传的就是list实例化对象的地址,最后默认由this指针接受。而我们需要的是对迭代器进行操作,固然需要this指向迭代器的指针。所以应该将迭代器封装起来到一个类里里更理想的状态。


所以迭代器封装成类的话,那么肯定需要的成员对象就是节点指针,而成员函数就一定有operator*和operator++。其实operator!=是最容易忽略的,仔细想想迭代器类也是一个自定义类型,这么可以直接去判断呢,所以operator!=也是要实现的,所以下边整理了需要实现的运算符重载函数以及其他的一些。

代码如下:

template<class T>
struct _List_iterator
{typedef ListNode<T> Node;_List_iterator(Node* node):_pnode(node){}T& operator*(){return _pnode->val;}_List_iterator<T> operator++()//前置++{_pnode = _pnode->next;return *this;}_List_iterator<T> operator++(int)//后置++{_List_iterator tmp = *this;_pnode = _pnode->next;return tmp;}_List_iterator<T> operator--(){_pnode = _pnode->pre;return *this;}_List_iterator<T> operator--(int){_List_iterator tmp = *this;_pnode = _pnode->pre;return tmp;}bool operator!=(const _List_iterator<T>& cmpnode)const {return _pnode != cmpnode._pnode;}T* operator->(){return &_pnode->val;//一般用于存自定义类型对象时,会隐藏一个箭头}Node* _pnode;
};


这里operator->的实现你可能会有疑问,举例下列代码:

class A
{
public:A(int a=0,int b=0):_a(a),_b(b){}int _a;int _b;
};
int main()
{cr::list<A> a;a.push_back(A(1, 1));//创建匿名对象初始化a.push_back(A(2, 2));a.push_back(A(3, 3));return 0;
}

面对这种情况,list实例化的是一个类的对象的话,而且想要打印出该类中成员变量的话,就是下面的方法:

cr::list<A>::iterator it = a.begin();
while (it != a.end())
{cout << (*it)._a << (*it)._b << endl;it++;
}

这里的(*it)就是list中的pnode->val也就是这个A类,但是并没有重载流运算符,所以就只能去访问里面的数据(公有),所以就是通过(.)运算符,既然可以用(.)那么肯定也可以用(->)所以就重载了(->)运算符,而(->)有点不一样:

T* operator->()
{return &_pnode->val;
}

返回值的类型的是A*一个类型,也就是返回一个指针,所以在调用(->)运算赋不得加上两个(->)了吗,一个是调用运算符重载函数返回A*,另一个是指针访问符,所以为了增强可读性就省略一个(->)

cr::list<A>::iterator it = a.begin();
while (it != a.end())
{cout << it->_a << it->_b << endl;it++;
}

以上两种写法实现结果都是一样的:



而对于begin和end函数面向的就是list实例化的对象,那么说就是在list类中实现就行

iterator begin()
{	//return iterator(_head->next);//匿名对象return _head->next;
}
iterator end()
{//return iterator(_head);//匿名对象return _head;
}

因为迭代器封装的类是单参数,所以可以直接返回节点的指针(会自动调用迭代器的构造函数)

 2.const迭代的实现

首先我们要了解list中const迭代器与非const迭代器的区别,const迭代器中的成员变量指向的数据是一个节点的指针,并不是说const修饰了以后这个节点不能改变,实际上是节点中的val成员变量不能改变。假如说是节点不能改变的话,那么就连pre和next这两个成员变量也不可以改变,如果连这两个都不能改变的话,那么还怎么实现operator++,还怎么遍历链表中的数据呢。

所以凭这一点,const迭代器就不可能是直接在iterator前面加上const限定符修饰那么容易。

而想要val的值不能改变的话,实际上就是operator*和operator->这两个函数的返回值要加const引用来修饰


方法一:再写一个_List_const_iterator的类

对于_List_terator类而言_List_const_iterator这个类只需要改一下类型而已,像operator*和operator->的返回值加上const修饰而其他需要返回迭代器的地方改成_List_const_iterator<T>就行,最后在list类中typedef  _List_const_iterator<T>  const_iterator就完成了


方法二:通过模版实现

方法一是需要再实现一个_List_const_iterator的类,但是本质上其实这个类相较于_List_iterator没什么区别,也就是返回值的类型有区别而已,所以此时对迭代器模版的引入就在合适不过了。而此时的模版参数肯定不能只有一个参数,而应该有三个参数:

template<class T,class ref,class ptr>

模版中第二个参数的实参其实就是const T&用于operator*运算符函数的返回值,而第三个参数的实参其实就是const T* 用于operator->函数的返回值。此时你可能会问为什么是需要这两个参数呢?其实判断方法很简单,就是通过实质上的比较const迭代器和非const迭代器实现时各函数返回值有哪些发生了改变。表面上你是只写了一个迭代器的类加一个模版就实现了两个迭代器干的活,实际上在编译的时候编译器通过模版参数自动生成了两个类,在使用时会优先找最匹配的调用。

所以这里提一点:模版中类名相同模版参数不同并不代表是同一个类型,类型=类名+模版参数

迭代器实现代码

template<class T,class ref,class ptr>struct _List_iterator{typedef ListNode<T> Node;_List_iterator(Node* node):_pnode(node){}ref operator*(){return _pnode->val;}ptr operator->(){return &_pnode->val;}_List_iterator<T,ref,ptr> operator++(){_pnode = _pnode->next;return *this;}_List_iterator<T,ref,ptr> operator++(int){_List_iterator tmp = *this;_pnode = _pnode->next;return tmp;}_List_iterator<T,ref,ptr> operator--(){_pnode = _pnode->pre;return *this;}_List_iterator<T,ref,ptr> operator--(int){_List_iterator tmp = *this;_pnode = _pnode->pre;return tmp;}bool operator!=(const _List_iterator<T,ref,ptr>& cmpnode)const{return _pnode != cmpnode._pnode;}Node* _pnode;};

其实上面的代码本质上就是将operator*和operator->的返回值虚拟化,然后在调用迭代器时才进行实例化的。 

	template<class T>class list{typedef ListNode<T> Node;//对类进行重命名public:typedef _List_iterator<T,T&,T*> iterator;//实例化typedef _List_iterator<T, const T&, const T*> const_iterator;const_iterator begin()const{  //return iterator(_head->next);//匿名对象return _head->next;}const_iterator end()const{//return iterator(_head);//匿名对象return _head;}......}

在list类中分别将两种类型迭代器重命名,所以在外面调用哪种迭代器时就会在内部模版实例化成具体的迭代器

3.list的插入与删除

        iterator insert(iterator pos,const T& x){Node* l1 = pos._pnode->pre;Node* l2 = pos._pnode;Node* tmp = new Node(x);l1->next = tmp;tmp->pre = l1;tmp->next = l2;l2->pre = tmp;return tmp;}iterator erase(iterator pos)//调用之后pos节点失效{Node* l1 = pos._pnode->pre;Node* l2 = pos._pnode->next;l1->next = l2;l2->pre = l1;delete pos._pnode;return l2;}void push_back(const T& x){insert(this->end(), x);//Node* tmp = new Node(x);//Node* tail = _head->pre;//找尾//tail->next = tmp;//tmp->pre = tail;//tmp->next = _head;//_head->pre = tmp;}

这里的插入删除同链表一样,没什么好说的,唯一要注意的就是迭代器失效的问题,list的insert不存在扩容换址,所以不会造成迭代器失效,但是list的erase就不同了,earase会释放当前的节点,所以调用之后就会失效。所以给erase一个返回值,返回需要删除的节点的下一个节点。

4.list的构造加赋值

        list():_head(nullptr){_head = new Node;//该节点也是一个类,创建时调用构造函数_head->next = _head;_head->pre = _head;}list(const list<T>& copy):_head(nullptr){_head = new Node;//该节点也是一个类,创建时调用构造函数_head->next = _head;_head->pre = _head;for (auto it : copy)//内部实际是用迭代器{push_back(it);}}list<T>& operator=(list<T> copy)//调用拷贝构造{std::swap(_head, copy._head);return *this;}

因为list容器类似带头双向循环链表,所以构造时要new一个带头节点,其次要注意的是new的这个节点的类型是我们自定义的类型,所以在new时就会自动调用该节点的构造函数将其空间内容初始化。赋值函数就实现的比较巧妙,通过传参调用拷贝构造函数的深拷贝,然后再换值就行。

5.空间释放析构函数

        void clear(){list::iterator del = this->begin();while (del != this->end()){del = erase(del);}_size = 0;}~list(){clear();delete _head;_head = nullptr;}

若有不恰当的描述欢迎留言指正!

http://www.ds6.com.cn/news/28371.html

相关文章:

  • 山西 网站建设网站恶意点击软件
  • 企业管理软件属于系统软件吗广州seo顾问服务
  • 如何做彩票网站的源码百度账号怎么注册
  • 汉中 wordpress汕头网站排名优化
  • 建设银行网站为什么打不开抖音矩阵排名软件seo
  • 网站建设中 图片怎么做推广网络
  • 安徽城乡建设部网站首页百度站长工具seo查询
  • 微型购物网站建设模板百度竞价被点击软件盯上
  • 用vps做网站的流程网络营销外包顾问
  • 真实的小企业网站建设网站排名优化软件联系方式
  • 外贸网站建设价格广州网站推广软件
  • 东软 网站群平台建设线上推广外包公司
  • 常州外贸网站设计百度seo优化系统
  • 上海个人做网站友情链接举例
  • 一个网站多少钱运营培训班学费大概多少
  • linux网站备份网络运营培训班多少钱
  • 做网站用win还是li360排名优化工具
  • 跟建设通一样的网站wp博客seo插件
  • 做网站需要跟客户了解什么软件免费python在线网站
  • 网站建设案例分享微信加精准客源软件
  • 顺飞网站建设怎么样北京seo培训机构
  • 广州11个区繁华排名济南seo网站排名关键词优化
  • 教学资源网站建设设计宁波网站seo哪家好
  • 祥云县外卖哪个网站厦门人才网官网招聘
  • 找做金融的网站seminar什么意思中文
  • 网站系统安全保护等级是必须做的网络销售挣钱吗
  • 国外做展台搭建的设计网站泉州seo代理商
  • 建网站域名后怎样做手机百度ai入口
  • 跨境电商网站系统开发人工智能培训机构
  • 广州新冠最新情况app搜索优化