C++ 20-span容器
C++ 20-span容器
span 是 C++20 中引入的一个新的标准容器,它类似于C++ 17的std::string_view,我们知道std::string_view可以创建std::string的一个视图,视图本身并不拥有实例,它只是保持视图映射的状态。在不修改实例的情况下,使用std::string_view会让字符串处理的性能大幅提升。实际上,对于那些连续的序列对象我们都可以创建这样一份视图,对于std::vector这样的对象可以提高某些操作中的性能,另外对原生数组可以提高其访问的安全性。而 span 用于表示连续的一段内存区间,类似于一个轻量级的只读数组容器。
它的定义在头文件 中,并位于 std 命名空间中。span 包含了一个指向连续内存区域的指针以及它所占用的大小,可以通过它来访问这个内存区域中的元素。
过去如果一个函数想接受无法确定数组长度的数组作为参数,那么一定需要声明两个参数:数组指针和长度:
1 |
|
这种人工输入增加了编码的风险,数组长度的错误输入会引发程序的未定义行为,甚至是成为可被利用的漏洞。C++20标准库为我们提供了一个很好解决方案std::span,通过它可以定义一个基于连续序列对象的视图,包括原生数组,并且保留连续序列对象的大小。例如:
1 |
|
除了原生数组,std::vector和std::array也在std::span的处理之列:
1 |
|
值得注意的是,std::span还可以通过构造函数设置连续序列对象的长度。
1 |
|
从std::string_view到std::span,我们可以看出C++标准库很乐于这种视图设计,因为这种设计和抽象的实现可以提高C ++程序的可靠性而又不牺牲性能和可移植性。
std::span的用法
span 主要用于以下场景:
- 作为函数的参数,用于指示函数需要处理的数据范围;
- 作为类的成员变量,用于表示对象所管理的内存区域;
- 作为数组的视图,用于访问数组的一部分
将数组转换为 span
1 |
|
这里将一个整型数组 arr 转换为 span 类型,并使用数组首地址和元素个数作为参数。
使用 span 来遍历一个容器
1 |
|
这里使用 span(vec) 来构造一个 span 对象,遍历其中的元素并输出。
使用 span 来获取子序列
1 |
|
这里将一个 span 对象 s 分割为从第 1 个元素开始,长度为 3 的子序列,并将结果存储到 s1 中。
将 span 转换为其他容器类型
1 |
|
这里使用 s.begin() 和 s.end() 将 span 对象 s 转换为迭代器范围,并使用这个迭代器范围构造一个 vector 容器 vec。
span 的底层原理
1 |
|
具体实现方式是通过指针来引用连续的一段内存,从而实现 span 的基本功能。由于 span 没有实际的内存所有权,所以它不能拥有或释放内存。它只是提供了对现有内存块的访问。
标准库中的 span 还提供了一些其他的功能,比如对子区间的切片和子区间的迭代器等。实际的实现可能会更加复杂,但其基本的思想是一致的。
span 与 array ,vector ,数组指针 的区别
span 与 array ,vector的区别
与 array 和 vector 相比,span 的主要区别在于它不拥有自己的存储空间,而是引用了另一个数组或容器的内存空间。因此,当我们需要使用一个连续的内存块时,可以使用 span 来代替 array 或 vector。
具体来说,array 是一个固定大小的数组容器,其大小在编译时就确定了,不能动态改变。vector 是一个动态增长的数组容器,可以动态分配内存,并在需要时扩大容量。而 span 是一个非拥有型的容器,可以看作是一个指向连续内存区域的引用,可以指向任何类型的元素。
在使用方面,array 和 vector 可以用来存储数据,并通过下标或迭代器来访问其中的元素;span 则更多地用来表示一段内存区域,并提供类似于迭代器的操作来访问其中的元素(就是 只读),如 begin、end、rbegin、rend 等。
span 与 数组指针的区别
- span是一个封装了数组指针和长度的轻量级容器,它提供了对数组的安全访问。指针只是一个指向内存位置的地址,没有长度信息。因此,使用指针时需要显式地传递长度信息,否则可能会导致缓冲区溢出等问题;
- span支持范围操作,它可以使用STL中的算法和其他支持范围操作的库进行操作。指针只能通过指针运算和下标操作来访问和操作数据;
- span是可传递性的,可以传递到函数中作为参数,而指针不能。这是因为在函数中传递指针时,我们必须显式地传递指针所指向的内存块的大小,否则函数无法确定内存块的大小;
- span是一个类模板,可以指定数据类型和长度类型。指针只能指向特定类型的数据。