C++初学者指南-5.标准库(第一部分)--顺序视图

news2024/9/17 8:23:32

C++初学者指南-5.标准库(第一部分)–顺序视图

文章目录

  • C++初学者指南-5.标准库(第一部分)--顺序视图
    • std::string_view (C++17)
      • 避免不必要的内存分配
      • 类似字符串的函数参数
      • 创建string_views
      • string_view接口
    • std::span (C++20)
      • 作为参数(主要用例)
      • 明确地划分跨度
      • 大小和数据访问
      • 比较Span
      • 从Span创建Span
    • 使用指南
      • 函数参数中的视图
      • 当心返回视图
      • 避免使用局部视图变量
    • 备忘录
    • 相关内容

视图不拥有资源
一个对象被称为资源(内存、文件句柄、连接、线程、锁等)的所有者,如果它对其生命周期(初始化/创建、结束/销毁)负责。

std::string_view (C++17)

#include <string_view>

  • 轻量级(= 复制成本低,可以按值传递)
  • 非拥有(= 不负责分配或删除内存)
  • 只读视图(= 不允许修改目标字符串)
  • 字符范围或字符串(类字符串)对象
  • 主要用例:只读函数参数(避免临时复制)

避免不必要的内存分配

动机:只读字符串参数
我们不希望为只读参数进行额外的复制或内存分配!
传统的选择 std::string const& 存在问题:

  • std::string可以从字符串字面值或char序列的迭代器范围构造。
  • 如果我们将一个对象作为函数参数传递,该对象本身不是字符串,但可以用于构造字符串,比如字符串文字或迭代器范围,那么将会分配一个新的临时字符串对象并绑定到常量引用上。

string_view避免了临时副本:
在这里插入图片描述

类似字符串的函数参数

如果你…使用参数类型
始终需要在函数内部保留输入字符串的副本std::string
传值
想要只读访问不一定需要一个副本
正在使用 C++17/20
#include <string_view>
std::string_view
想要只读访问不一定需要一个副本
被困在C++98/C++11/C++14标准
std::string const&
传递常量引用
希望可以直接在原字符串上修改(尽量避免使用输出参数)std::string &
传递非常量引用

看这里的更多解释

创建string_views

使用构造函数调用

std::string s = "Some Text";
// view whole string
std::string_view sv1 { s };
// view subrange
std::string_view sv2 {begin(s)+2, begin(s)+5};
std::string_view sv3 {begin(s)+2, end(s)}; 

运行示例代码

带有特殊标记的字面值 "…"sv

using namespace std::string_view_literals;
auto literal_view = "C-String Literal"sv;
cout << literal_view;

运行示例代码

注意:视图可能会比字符串存在的时间更长!

std::string_view sv1 {std::string{"Text"}};
cout << sv1; //  字符串对象已经被释放!
using namespace std::string_literals;
std::string_view sv2 {"std::string Literal"s};
cout << sv2; //  字符串对象已经被释放!

主要应该把 string_view 作为函数参数使用!

string_view接口

在这里插入图片描述
std::string_view指南图表
在这里插入图片描述

std::span (C++20)

#include < span >

  • 轻量级(= 复制成本低,可以按值传递)
  • 非拥有视图(= 不负责分配或删除内存)
  • 一个连续的内存块(如std::vector,std::array等)
    主要用途:作为函数参数(与容器无关的值访问)
span< int >可更改其值的整数序列
span sequence of integers whose values can be changed
可更改其值的整数序列
span< int const >无法更改其值的整数序列
span<int,5>由5个整数组成的序列(编译时固定的值的数量)

在这里插入图片描述

作为参数(主要用例)

void print_ints  (std::span<int const> s);
void print_chars (std::span<char const> s);
void modify_ints (std::span<int> s);

用容器/范围进行调用:

std::vector<int> v {1,2,3,4};
print_ints( v );  
std::array<int,3> a {1,2,3};
print_ints( a );  
std::string s = "Some Text";
print_chars( s );  
std::string_view sv = s;
print_chars( sv );  

运行示例代码

使用迭代器范围进行调用:

std::vector<int> v {1,2,3,4,5,6,7,8};
// iterator range:
print_ints( {begin(v), end(v)} );  
print_ints( {begin(v)+2, end(v)} );  
print_ints( {begin(v)+2, begin(v)+5} );  
// iterator + length:
print_ints( {begin(v)+2, 3} );  

span将序列数据的存储策略与只访问序列中的元素的代码解耦但不改变其结构。

明确地划分跨度

整个容器/范围的视图:

std::vector<int>  w {0, 1, 2, 3, 4, 5, 6};
std::array<int,4> a {0, 1, 2, 3};
// 自动推断类型/长度:
std::span sw1 { w };  // span<int>
std::span sa1 { a };  // span<int,4>
// 明确的只读视图:
std::span sw2 { std::as_const(w) };
// 带有明确类型参数:
std::span<int>       sw3 { w }; 
std::span<int>       sa2 { a };
std::span<int const> sw4 { w };
// 使用显式类型参数和长度:
std::span<int,4> sa3{ a };

运行示例代码

容器子序列的视图

vector<int> w {0, 1, 2, 3, 4, 5, 6};
//                   |----.---'
std::span s1 {begin(w)+2, 4}; 
std::span s2 {begin(w)+2, end(w)}; 

运行示例代码

大小和数据访问

std::span<int> s =;
if (s.empty()) return;
if (s.size() < 1024) {}
// spans in range-based for loops
for (auto x : s) {}
// indexed access
s[0] = 8;
if (s[2] > 0) {}
// iterator access
auto m1 = std::min_element(s.begin(), s.end());
auto m2 = std::min_element(begin(s), end(s));

运行示例代码

比较Span

#include <algorithm>  // std::ranges::equal
std::vector<int> v {1,2,3,4};
std::vector<int> w {1,2,3,4};
std::span sv {v};
std::span sw {w};
bool memory_same = sv.data() == sw.data();  // false
bool values_same = std::ranges::equal(sv,sw);  // true

运行示例代码

从Span创建Span

std::vector<int> v {0,1,2,3,4,5,6,7,8};
std::span s = v;
auto first3elements = s.first(3);
auto last3elements  = s.last(3);
size_t offset = 2;
size_t count = 4;
auto subs = s.subspan(offset, count);

运行示例代码

使用指南

函数参数中的视图

  • 将函数实现与数据表示/容器类型分离
  • 清楚地传达只读取/修改序列中的元素,而不修改底层的内存/数据结构的意图
  • 可以轻松地将函数应用于序列子范围
  • 几乎永远不会悬空,即指向已被销毁的内存(因为参数的生存周期超过所有函数局部变量)
int foo (std::span<int const> in) { … }
std::vector<int> v {…};
// v永远比参数 'in' 存活得长久!
foo(v);
foo({begin(v), 5});
  • 视图的目标不能让函数执行期间视图引用的内存失效(除非它是在另一个线程中执行的)。
  • 视图可以通过避免一级间接访问加快访问速度:
    在这里插入图片描述

当心返回视图

  • 视图指向的对象或内存不是总是清晰的
  • 返回的视图可能会(无意中)失效
// 哪个参数是span返回值的目标?
std::span<int const>
foo (std::vector<int> const& x, std::vector<int> const& y);
// 我们可以假设返回的span
// 指向vector的元素
std::span<int const> random_subrange (std::vector<int> const& v);
// 然而,这仍然存在问题:
auto s = random_subrange(std::vector<int>{1,2,3,4});
// 's' 悬空了 - 向量对象已经销毁了!
class Payments {public:
  std::span<Money const> of (Customer const&) const;};
Customer const& john =;
Payments pms = read_payments(file1);
auto m = pms.of(john);
pms = read_payments(file2);
// 根据支付的完成情况
// 在重新赋值之后,可能 m 的目标内存不再有效

避免使用局部视图变量

  • 易产生悬空视图,因为我们必须手动跟踪生命周期,确保没有视图超过其目标。
  • 即使内存所有者仍然存活,它可能会使视图引用的内存无效。
std::string str1 = "Text";
std::string_view sv {str1};
if () {
  std::string str2 = "Text";
  sv = str2;
}
cout << sv; //  str2 已经被释放!
std::string_view sv1 {"C-String Literal"};
cout << sv1; // 正确
std::string_view sv2 {std::string{"Text"}};
cout << sv2; // 错误 string对象已经被释放!
using namespace std::string_literals;
std::string_view sv3 {"std::string Literal"s};
cout << sv3; //  错误 string对象已经被释放!

所有者的内存失效
像 vector 这样的容器可能会分配新的内存 使它的所有视图无效:

std::vector<int> w {1,2,3,4,5};
std::span s {w};
w.push_back({6,7,8,9});
cout << s[0]; //  w 可能重新分配了内存

备忘录

在这里插入图片描述
在这里插入图片描述

相关内容

std::span
使用哪种字符串函数参数类型?
cppreference的std:span参考
cppreference的std::string_view参考
striing_view的视频教程
string_view是一种借用类型(by Arthur O’Dwyer)
用值传递 std::string_view 的三个原因(by Arthur O’Dwyer)
关于通过值传递 std::string_view 的三个理由的补充(by Arthur O’Dwyer)

附上原文链接
如果文章对您有用,请随手点个赞,谢谢!^_^

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1918062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0301STM32GPIO外设输出功能

STM32GPIO外设输出功能 STM32内部的GPIO外设GPIO简介基本结构GPIO位结构输入部分&#xff1a;输出部分&#xff1a; GPIO八种工作模式浮空/上拉/下拉输入模拟输入开漏/推挽输出复用开漏/推挽输出 手册寄存器描述English缩写GPIO功能描述外设的GPIO配置GPIO寄存器描述端口输入数…

TSN时钟同步 | PTP对时案例演示——基于NXP i.MX 8M Plus

TSN简介 TSN介绍 时间敏感网络(TSN&#xff1a;Time Sensitive Networking)是IEEE802.1工作组中的TSN任务组开发的一套协议标准。该标准定义了以太网数据传输的时间敏感机制&#xff0c;为标准以太网增加了确定性和可靠性&#xff0c;以确保以太网能够为关键数据的传输提供稳…

Keil 5 在编译代码时出现CPU占用过高、伴随出现keil卡顿未响应的问题解决办法

问题背景&#xff1a; 在使用keil 5编译程序的时候&#xff0c;经常出现CPU占用100%&#xff0c;并且伴随出现keil卡顿未响应情况。 如下图所示&#xff1a; 解决方法1&#xff1a; 修改keil的多线程编译设置&#xff0c;减少编译的线程数或者不使用多线程编译&#xff08;编译…

【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(三)

上一篇圆形表盘指针式仪表的项目受到很多人的关注&#xff0c;咱们一鼓作气&#xff0c;把数字式工业仪表的智能读数也研究一下。本篇主要讲如何用YOLOV8实现数字式工业仪表的自动读数&#xff0c;并将读数结果进行输出&#xff0c;若需要完整数据集和源代码可以私信。 目录 &a…

交易伦敦银系统,听说高手都有一套

成功的伦敦银交易者都有一套自己的交易系统&#xff0c;这个系统为他们提供了一个明确的、可重复的决策框架&#xff0c;无论白银市场如何波动&#xff0c;他们都能按照既定的规则和策略进行操作&#xff0c;避免了情绪化决策和随意交易——这样的一致性有助于减少错误和亏损&a…

[激光原理与应用-108]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 11 - 焊接工艺概述之准备知识

目录 前言&#xff1a; 一、准备知识 1.1 焊接前的处理 1.2 激光焊接原理 1、激光焊接基本原理 2、熔池的形成 3、熔池的流动与凝固 4、影响熔池质量的因素 5、激光焊接熔池的优势 1.3 激光焊接技术 1.4 焊接工艺难题的解析步骤 前言&#xff1a; 焊接工艺的主要任…

机器学习(五) -- 监督学习(6) --逻辑回归

系列文章目录及链接 上篇&#xff1a;机器学习&#xff08;五&#xff09; -- 监督学习&#xff08;5&#xff09; -- 线性回归2 下篇&#xff1a;机器学习&#xff08;五&#xff09; -- 监督学习&#xff08;7&#xff09; --SVM1 前言 tips&#xff1a;标题前有“***”的内…

location匹配的优先级和重定向

nginx的重定向&#xff08;rewrite&#xff09; location 匹配 location匹配的就是后面的uri /wordpress 192.168.233.10/wordpress location匹配的分类和优先级 1.精确匹配 location / 对字符串进行完全匹配&#xff0c;必须完全符合 2.正则匹配 ^-前缀级别&#xff…

cadence23 中 板框的定义与 .dxf板框文件的添加

新建board板子的图纸&#xff1a; 板框定义在board geometry 中的 Design_Outline中&#xff1a; 点击添加矩形&#xff1a; 查看选项&#xff0c;将选项卡由 绘制矩形 改为 放置矩形 &#xff0c;自定义宽度和长度&#xff1b; 选择放置在板框层&#xff1a; 即放置成功&a…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥派生(C/C++)】

密钥派生(C/C) 以HKDF256密钥为例&#xff0c;完成密钥派生。具体的场景介绍及支持的算法规格&#xff0c;请参考[密钥生成支持的算法]。 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 生成密钥 指定密钥别名。 初始化密钥属…

【TB作品】51单片机 Proteus仿真 基于单片机的LCD12864万年历及温度监测系统设计

实验报告&#xff1a;基于单片机的LCD12864万年历及温度监测系统设计 背景介绍 本实验旨在设计并实现一个基于STC89C52单片机的LCD12864显示的万年历系统&#xff0c;同时集成温度传感器以实现温度监测功能。系统具备整点报时和闹钟功能&#xff0c;通过蜂鸣器进行提示。该设…

继电器实现直流电机正反转

有关继电器的使用方法&#xff0c;首先介绍了继电器的内部工作原理&#xff0c;然后介绍了两只继电器组成的正反转电路&#xff0c;以及用继电器实现直流电机正反转的具体方法&#xff0c;供大家学习参考。 继电器实现直流电机正反转 1、继电器内部原理 线圈断电时公共与常闭…

从微分方程组构建 bbr 模型

描述分析 bbr 的文字自 2016 年底起至今从空白到泛滥&#xff0c;我自己在期间贡献了不少&#xff0c;本文又是一篇&#xff0c;但不同的是&#xff0c;本文尝试用闭环的数学模型给出一个 bbr 的全貌&#xff0c;顺便和 aimd 做对比。 先看带宽特性 bw(t)&#xff0c;设瓶颈带…

28V飞机库维修电源在飞机库中的作用

飞机库作为飞机停放和维护的重要场所&#xff0c;其设施的完善和电源系统的稳定运行是保证飞机正常运行的前提。随着我国航空事业的飞速发展&#xff0c;飞机维修行业面临着越来越大的挑战。在飞机维修过程中&#xff0c;电源系统作为关键组成部分&#xff0c;其稳定性和可靠性…

let/const/var的区别及理解

在JavaScript中&#xff0c;let、const 和 var 是用来声明变量的关键字&#xff0c;但它们之间在作用域、变量提升、重复声明等方面存在区别&#xff0c;详细情况如下: 1. let、const、var 的区别 (1) 块级作用域 let 和 const&#xff1a;具有块级作用域&#xff0c;由 {} 包…

系统服务综合项目

要求&#xff1a; 现有主机 node01 和 node02&#xff0c;完成如下需求&#xff1a; 1、在 node01 主机上提供 DNS 和 WEB 服务 2、dns 服务提供本实验所有主机名解析 3、web服务提供 www.rhce.com 虚拟主机 4、该虚拟主机的documentroot目录在 /nfs/rhce 目录 5、该目录由 no…

前端JS特效第29波:jQuery鼠标经过星星显示特效

jQuery鼠标经过星星显示特效&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下&#xff1a; <!doctype html> <html lang"zh"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content&q…

TomCat服务器安装和配置教程

1.TomCat下载路径 TomCat官方网站&#xff1a;http://tomcat.apache.org 前往该网站下载安装tomcat&#xff0c;tar.gz文件是Linux操作系统的安装版本&#xff0c;zip文件是 windows操纵系统的压缩版本 打开后是如下网站&#xff0c;请下载匹配的操作系统的文件并且选择合适的…

Selenium原理深度解析

在自动化测试领域&#xff0c;Selenium无疑是最受欢迎和广泛使用的工具之一。它支持多种浏览器和操作系统&#xff0c;为开发人员和测试人员提供了强大的自动化测试解决方案。本文将深入探讨Selenium的工作原理&#xff0c;包括其架构、核心组件、执行流程以及它在自动化测试中…

IGBT功率半导体的主要用途及全球知名厂商

功率半导体作为现代电子电力系统的核心组件&#xff0c;其技术的不断发展和创新对于提高能源利用效率、推动新能源产业发展以及实现工业自动化和智能化具有重要意义。 IGBT功率模块市场预测 近年来&#xff0c;IGBT功率半导体的热度持续攀升。据QYResearch调研团队最新报告“全…