Re 从零开始的C++之路(四)容器和迭代器

    前面一节了解了字符串,今天学习容器以及迭代器的相关操作。容器和字符串一样,都属于C++标准库里的标准库类型。形如其名,容器可以理解为专门用来存储数据的容器状的数据结构。是指定类型的数据的集合,并且可以通过索引来访问。这是一种十分方便的工具,你不用担心定义多大的空间,也不用去写很多相关的操作。以后标注*的点大致是我认为需要注意的点,不过还是建议大家都看看,不要分重点和非重点。因为C++这种语言它的复杂性,很可能略过的就是有用的。


*1.类模板 

容器是一种叫做类模板的东西。比如,每一个饭店都会有菜谱,大致的范围有炒菜、凉菜、汤、甜品啥的。有菜名,有菜价也有饭店的名字。但是如果你是制作菜谱的复印社,如果每一个来的顾客你都要重新写一遍这些东西,那就会浪费相当多的时间。所以就有了模板,大致把共有的特征制作成一个模板,然后稍加改动就可以直接使用。C++就存在着这些模板,当你并不知道你所面对的具体类型或者细节的时候,写一种抽象的模板,然后使用的时候再实例化。在容器中,使用< >来实例化。注意的是使用容器需要<vector>头文件的支持。

vector<int> v1;//v1是专门用来保存整型的容器

vector<char> v2;//v2是用来保存char型变量

vector<vector<int>>v3;//v3是用来保存(保存整型变量的容器)的容器 这里就形成了一种二维结构

值得注意的是,因为vector能用来存储大部分类型的变量对象,但是引用并不是对象,所以不存在存储引用的容器。


2.定义初始化容器(重点看看带**的,剩下酌情忽略)

跟字符串类似,下面提出几种初始化方式。T 代表一种特定的类型

vector<T> v1;//默认初始化,里面是空的  **

vector<T> v2(v1);//参照字符串,这里是直接初始化 v2和v1是一致的 注意 这里的v1不能是字面值 因为容易和v5发生误解 **

vector<T> v3 = v1;//拷贝初始化,这里看起来和v2相同  **

vector<T> v4(n,value);//字面意思,产生的v4里有n个元素,值为value **

vector<T> v5(n);//和v4类似,只是每个value被默认初始化了

vector<T> v6{a,b,c,d,.....};//也是记字面意思,括号里面有多少就会有多少个量,量的值对应括号里的值。直接初始化

vector<T> v7={a,b,c,d.....};//这里的效果等价于v6 这里的括号应该是属于列表初始化,参照Re C++ (一) 。拷贝初始化 **

使用的时候,一般默认初始化一个空的容器,然后向里面加元素是常用的使用场景。也比较方便。和其他变量的初始化类似。

一般来看用{ }初始化的方式,将{ }里的值当做元素的值来处理。用( )初始化的一般当做某种形式的限制。但是也存在下列情况

vector<string> v{15};//注意区分 这里是整型15 并不是"15"。这里的显然不能用整数来初始化string。这里编译器会尝试使用()来构造v。使之等价成为 

 vector<string> v(15);//拥有15个默认初始化元素的容器

诚然编译器可能会存在类似的操作试图理解你,但是明知道这样还要这么用的,实在是属于没事找事干。  


*3.对于vector的基本操作

向vector中添加元素,注意是向容器的尾部添加元素。用push_back()函数

vector<T> v;

T t;

v.push_back(T);

添加的时候不用顾虑容器是否会满。C++标准要求,vector必须能高效的增长。所以不用太顾虑它的大小,它会自己调节来满足你的要求。但是如果是很庞大的数据还是谨慎一些,不过平时一般处理一些问题足够了。


其他一些的操作

v.empty();//是否是空

v.size();//当前拥有多少元素

v[n];//取索引为n的元素 从0开始

v = {a,b,c····};//这里用括号里的元素替换掉原来v中的内容

v1 == v2;//是否相等

v1 != v2;//是否不同

<,<=,>,>=//参照string类型,按照字典序排序


参照在string中学习到的范围for的使用,这里也可以针对所以vector中的元素使用

for(auto &r : v)

    r的相关操作;

注意到size()运算返回的值 也是size_type型的 但是 这里将会是vector<T> :: size_type型的。和string::size_type的提醒一样不要混入带符号数进去,同时可以用auto轻松搞定。

对于v[n]操作 一定是对已经存在在容器中的元素索引进行操作。比如你又4个元素 你现在执行v[4] = 4;这里肯定歇菜,因为容器中目前大小就是4 你当然不能访问并不存在的第5个位置 正确的做法是 v.push_back(4);


4.迭代器

前面对string和vector都存在取下标索引对元素进行操做。对于string和其他存在的几种容器存在迭代器这种神奇的东东。过去我们知道指针是获取了对象的内存地址,对内存地址进行操作。迭代器是通过获得类成员中的begin和end成员(这两种成员在string和容器中定义),和字面的意思一样,begin是第一个元素的迭代器,end自然就是最后一个元素的下一个位置!!!不是最后一个元素。如果begin和end相等就说明这个容器是空的。迭代器到底是什么类型不用关心,记住怎么使用就够了。相当于一种标记,和指针实不同,但形类似,貌似更便捷?迭代器使用auto就可以了。


*5.迭代器的用法

*iter;//类似于指针的解引用  返回的是所代表的对象

iter->m;//也是和指针一样 返回的是iter所代表的对象的m元素的值 等价于 (*iter).m 

++iter;//让iter向后一个 

--iter;//让iter向前一个  

iter1 == iter2;//是否相当 

iter1 !=  iter2;//是否不等 

iter+n;// 向后移动n个单位

iter-n;// 向前移动n个单位

iter1 - iter2;//得到的将是他们之间的距离 比如end() - begin()是所有元素的数量

<,>,<=,>=;//参照意思自己理解


使用迭代器处理在字符串中使用范围for处理的程序

string s = "Hello"

auto iter = s.begin();//一般这样获取迭代器

while(iter != s.end)

{

    *iter = toupper(*iter);//转成大写字母

    ++iter;//指向下一个目标

}

     今天认识到了一种标准库里方便的工具--容器。容器真的是一种很方便的集合工具。下面说点自己最近的感悟,一直比较喜欢逛知乎关于程序员的帖子。这几天看了关于是否有学习C或者C++的必要的帖子。对于C和C++这哥俩,我认为他们是最接近机器的高级语言,所以有的时候它们的思维逻辑总让人觉得怪怪的。对于这两种的语言的学习,是需要对机器执行大致过程有一定的了解的。不然就会存在难以理解指针这种劝退新手的问题。通过这次研究生考试复试,我算是大致回忆了一下操作系统和一些如微机这种课的知识。现在意识到,成为一名武林高手,强大的内力是无法速成的。想成为一个优秀的C++程序员,我们以后面对的是十分细致十分复杂的底层问题,很多这种注重效率并且经常机器打交道的事,需要扎实的基本功,比如堆区、栈区这些基本常识等。以后还会涉及到多线程,使用GPU做通用计算这些需要十分细致的思维和无比的耐心的问题。有可能一个地方的空间开辟的小了一点,一处悬空了指针,这时候没法指望傻狍子一样的编译器能检查出来这种问题。解决这些问题需要彻底深入的了解无论是算法逻辑还是实际的机器过程,细致入微。想成为不会被淘汰的那一个,就必须要成为那个无法被替代的那一个。我一直认为C++君和C君有着无穷的魅力,指尖所触的,是机器的每一寸血脉,每一根神经。对于无脑吹JAVA等(易语言就是个笑话不提了)的或者某一种语言万能,而C++这种反人类的语言不值得学习的呼声(这里不是黑JAVA,本人还是十分喜欢JAVA在某些开发场合的高效率,但自己就是个C++脑残粉),以及现在的大红大紫的被文科生吹捧出来的人工智能,劝君多慎重,选择一条适合自己的路很难。坚持自己所坚持的,把每一步迈的坚实。选择C++是一条无比牛逼的路(我反正这么觉得),这同时也需要付出更多,但是收获的更多。

诸君晚安。明天OpenCV的书大概就到了,就可以开始继续CV的学习笔记了。中间可能捎带一些有关图像处理基础算法的介绍,具体看学习进度而定。今晚和本科室友聊了一阵,羡慕优秀的他们,自己休息一下也应该努力追赶。祝福色鸭、土豪、邵老师工作学习顺利,喝冰红茶常中再来一瓶啥的,苟富贵勿相忘。




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要为自定义的C序列式容器添加迭代器,需要定义一个迭代器结构体,包含容器的元素类型、指向当前位置的指针(或索引)、向前移动迭代器的函数指针、向后移动迭代器的函数指针、获取当前元素的函数指针等等。接下来,需要在容器结构体中添加一个函数,用于返回迭代器对象。该函数应该接受容器的指针作为参数,并返回一个迭代器对象。 下面是一个简单的示例代码,用于向自定义的C序列式容器中添加迭代器: ```c typedef struct { int* data; int size; } my_container; typedef struct { int* ptr; int index; } my_iterator; my_iterator* create_iterator(my_container* c) { my_iterator* it = malloc(sizeof(my_iterator)); it->ptr = c->data; it->index = 0; return it; } void advance_forward(my_iterator* it) { it->ptr++; it->index++; } void advance_backward(my_iterator* it) { it->ptr--; it->index--; } int get_current_element(my_iterator* it) { return *(it->ptr); } // 容器结构体 typedef struct { int* data; int size; my_iterator* (*begin)(my_container*); } my_sequence; my_iterator* begin(my_container* c) { return create_iterator(c); } my_sequence* create_sequence(int* data, int size) { my_sequence* s = malloc(sizeof(my_sequence)); s->data = data; s->size = size; s->begin = begin; return s; } int main() { int data[] = {1, 2, 3, 4, 5}; my_sequence* s = create_sequence(data, 5); my_iterator* it = s->begin(s); while (it->index < s->size) { printf("%d\n", get_current_element(it)); advance_forward(it); } return 0; } ``` 在上面的示例代码中,我们定义了一个名为`my_iterator`的迭代器结构体,它包含一个指向当前元素的指针和一个当前位置的索引。然后,我们定义了一些用于操作迭代器的函数,例如向前移动、向后移动和获取当前元素。接下来,我们定义了一个名为`my_sequence`的容器结构体,它包含一个指向数据的指针和数据的大小。然后,我们在容器结构体中定义了一个函数`begin`,该函数返回容器迭代器对象。最后,我们在`main`函数中使用容器迭代器进行了简单的测试。 当然,以上只是一个简单的示例,实际情况中需要根据具体的需求来定义迭代器的结构体和操作函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值