STL的容器类和迭代器(2)

一、迭代器的几个细节问题

1、const与非const

(1)begin和end返回可读可写的迭代器,而cbegin和cend返回const的只读迭代器
在这里插入图片描述
(2)代码验证迭代器的读写权限

#include <iostream>
#include <array>

using namespace std;
int main(int argc, char *argv[])
{    
    array<int, 3> a = {1, 2, 3};
#if 0
    array<int, 3>::iterator iter;//可读可写
    array<int, 3>::const_iterator citer;

    for(iter = a.begin(); iter != a.end(); iter++)
    {
        cout << "iter = " << iter << endl;
        cout << "*iter = " << *iter << endl;
        *iter = 11;
    }
    
    for(citer = a.cbegin(); citer != a.cend(); citer++)
    {
        cout << "citer = " << citer << endl;
        cout << "*citer = " << *citer << endl;
    }

#else
    for(auto iter = a.begin(); iter != a.end(); iter++)
    {
        cout << "iter = " << iter << endl;
        cout << "*iter = " << *iter << endl;
        *iter = 11;
    }
    
    for(auto citer = a.cbegin(); citer != a.cend(); citer++)
    {
        cout << "citer = " << citer << endl;
        cout << "*citer = " << *citer << endl;
    }
#endif

    return 0;
}

2、begin和end的半开半闭区间

(1)begin返回第0个元素的迭代器(类似于C数组的首元素首地址指针)

(2)end指向的不是末尾元素的迭代器,而是末尾元素的(实际不存在的)下一个元素的迭代器

(3)前闭后开区间,经常记做[begin end),这样设计是为了写循环遍历时方便
在这里插入图片描述

3、正向和逆向迭代器

(1)rbegin和rend返回逆向迭代器

(2)逆向迭代器的begin是最末尾元素,而end是第0个元素去(实际不存在的)前面1个元素的迭代器

(3)逆向迭代器++是向前移动,而–是向后移动
在这里插入图片描述
所谓逆向是指数据存储的顺序变了,例如原来地址由低到高存着1、2、3,逆向后,地址由低到高存着3、2、1。begin开始的哪里一直是闭区间,end一直是开区间。

#include <iostream>
#include <array>
#include <cstdio>

using namespace std;
int main(int argc, char *argv[])
{    
    array<int, 3> a = {1, 2, 3};

    for(auto iter = a.begin(); iter != a.end(); iter++)
    {
        cout << "iter = " << iter << endl;
        cout << "*iter = " << *iter << endl;
    }
    
    for(auto citer = a.crbegin(); citer != a.crend(); citer++)
    {
        cout << "*citer = " << *citer << endl;
        //cout << "citer = " << citer << endl;//使用cout打印逆向迭代器的地址,编译报错,类型不匹配
    }

    return 0;
}

4、迭代器越界会怎么样

(1)和数组越界类似,编译时不审查,运行时会崩溃

(2)不管是正向还是逆向迭代器,++不到end,–不到begin,就不会越界

二、STL的不同类型迭代器

1、C++17前共有5种迭代器

(1)InputIterator,输入迭代器。只能从容器内读出而不能向容器内写入,只能单次读出(读出过一次后不保证再次操作仍然可以,想想流输入输出),只能++走不能–走(就是单向的),不能保证第二次遍历容器时,顺序不变。输入迭代器适用于单通只读型算法。

(2)OutputIterator,输出迭代器。用于将信息传输给容器(修改容器中元素的值),但是不能读取。(显示器就是只能写不能读的设备,可用输出容器来表示它)只能++走不能–走(就是单向的),输出迭代器适用于单通只写型算法。

(3)ForwardIterator,前向迭代器。只能++走不能–走(就是单向的)

(4)BidirectionalIterator,双向迭代器。既能++也可以–,双向移动。

(5)RandomAccessIterator,随机访问迭代器。能双向移动,并且可以单次跨越多个元素移动。

2、C++17新增1种迭代器

(1)contiguousIterator,连续迭代器。所指向的逻辑相邻元素也在内存中物理上相邻。

3、STL的6种迭代器总结

(1)每种迭代器更应该被看作是具有某些预定义特征(或者满足某接口要求)的一个迭代器的实现。

(2)这些迭代器彼此之间有功能重叠,譬如随机访问迭代器可由双向迭代器扩展而来,详见文档:https://zh.cppreference.com/w/cpp/iterator

(3)为何定义多种迭代器?
  是为了适配容器特性和泛型算法,后面会看到array的迭代器既是一个双向迭代器,也是一个随机访问迭代器。

4、C++20的新迭代器

(1)C++20中重新实现了基于concept的新的迭代器体系

(2)原有的模板都被加了前缀Legecy,但很长时间仍然可用,甚至还是主流

(3)基于concept的新迭代器主要在类型约束和模板特化方面做了优化

(4)C++20目前还刚开始,可以先不管,先学好原有的,后面再扩展去学C++20新特性

三、序列容器之Vector

参考学习文档:https://zh.cppreference.com/w/cpp/container/vector

1、Vector的特征

(1)Vector和Array相同点是:都是数组、都是contiguousIterator、容器内元素种类都相同

(2)Vector和Array不同点是:Array是固定数组;Vector是动态数组,可以按需扩展数组大小

(3)vector 的存储是自动管理的,按需扩张收缩。

(4)vector 通常占用多于静态数组的空间,因为要分配更多内存以管理将来的增长

(5)vector 所用的方式不在每次插入元素时,而只在额外内存耗尽时重分配。

2、构造拷贝和赋值

在这里插入图片描述

容量相关的成员函数:
size:返回容纳的元素数,当前的元素数
max_size:返回可容纳的最大元素数,即可扩充的最大元素数,因为vector是动态数组
capacity:返回当前存储空间能够容纳的元素数

C++11 noexcept: https://blog.csdn.net/luoshabugui/article/details/86255100
C++ explicit: https://blog.csdn.net/guoyunfei123/article/details/89003369

(1)多种参数的构造、拷贝构造

https://zh.cppreference.com/w/cpp/container/vector/vector
其具有多种拷贝构造函数,若遇到没有见过的初始化方式,可以查看属于哪一种构造函数。
拷贝构造函数,要考虑到深拷贝和浅拷贝	

(3)assign
在这里插入图片描述

3、容器元素的遍历

(1)最本质的笨办法:获取容器迭代器,再写for/while循环来遍历
(2)C++11:Range-Based for循环,案例参考assign函数页面

#include <vector>
#include <iostream>
 
int main()
{
    std::vector<char> characters;
 
    characters.assign(5, 'a');
 
    for (char c : characters) {
        std::cout << c << ' ';
    } 
 
    characters.assign({'\n', 'C', '+', '+', '1', '1', '\n'});
 
    for (char c : characters) {
        std::cout << c;
    }
}

输出:
a a a a a 
C++11

  本容器的其他一些操作方法与array相同,可查看该类相关的文档,了解使用:
https://zh.cppreference.com/w/cpp/container/vector
在这里插入图片描述

#include <iostream>
#include <vector>
using namespace std;

int main()
{
   // 创建一个向量存储 int
   vector<int> vec; 
   int i;
 
   // 显示 vec 的原始大小
   cout << "vector size = " << vec.size() << endl;
 
   // 推入 5 个值到向量中
   for(i = 0; i < 5; i++){
      vec.push_back(i);
   }
 
   // 显示 vec 扩展后的大小
   cout << "extended vector size = " << vec.size() << endl;
 
   // 访问向量中的 5 个值
   for(i = 0; i < 5; i++){
      cout << "value of vec [" << i << "] = " << vec[i] << endl;
   }
 
   // 使用迭代器 iterator 访问值
   vector<int>::iterator v = vec.begin();
   while( v != vec.end()) {
      cout << "value of v = " << *v << endl;
      v++;
   }
 
   return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:

vector size = 0
extended vector size = 5
value of vec [0] = 0
value of vec [1] = 1
value of vec [2] = 2
value of vec [3] = 3
value of vec [4] = 4
value of v = 0
value of v = 1
value of v = 2
value of v = 3
value of v = 4

关于上面实例中所使用的各种函数,有几点要注意:
push_back( ) 成员函数在向量的末尾插入值,如果有必要会扩展向量的大小。
size( ) 函数显示向量的大小。
begin( ) 函数返回一个指向向量开头的迭代器。
end( ) 函数返回一个指向向量末尾的迭代器。

四、序列容器之list

参考学习:https://zh.cppreference.com/w/cpp/container/list
(1) list 通常实现为双向链表。
在这里插入图片描述
在这里插入图片描述
insert、emplace的效果是相同的,但实现过程是有差别的,从二者的参数可以看出。
在这里插入图片描述
  以上成员函数的具体使用方法与例程,请打开上边提供的网址,该网址大家可以保留下。便于自己在进行C++开发时,及时查询相关的知识。

#include <iostream>
#include <list>

using namespace std;

int main(int argc, char **argv)
{
    list<int> l = {1, 2, 3, 4, 5, 6}; 

    cout << "l.size = " << l.size() << endl;
    cout << "l.max_size = " << l.max_size() << endl;
    cout << "is or not empty :" << l.empty() << endl;

    for(auto c : l)
    {
        cout << c << ' ';
    }
    cout << endl;

    l.assign(6, 0);

    l.push_front(1);
    l.push_back(9);
    l.insert(l.begin(), 0);
    l.sort();
    
    for(auto iter = l.begin(); iter != l.end(); iter++)
    {
        cout << *iter << ' ';
    }
    cout << endl;

    return 0;
}

五、序列容器之deque

  deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。

参考学习:
https://blog.csdn.net/u010710458/article/details/79540505
https://zh.cppreference.com/w/cpp/container/deque

#include <iostream>
#include <deque>
 
int main()
{
    // 创建容纳整数的 deque
    std::deque<int> d = {7, 5, 16, 8};
 
    // 从 deque 的首尾添加整数
    d.push_front(13);
    d.push_back(25);
 
    // 迭代并打印 deque 的值
    for(int n : d) {
        std::cout << n << '\n';
    }
}
输出:
13
7
5
16
8
25

注:本文章参考了《朱老师物联网大讲堂》课程笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值