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

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)

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

笔记本硬盘数据恢复的6种方法!简单易懂

可以从笔记本电脑硬盘恢复已删除的数据吗&#xff1f; “我不小心删除了笔记本电脑硬盘上的重要数据。请问我可以在笔记本电脑硬盘上恢复已删除的数据吗&#xff1f;如果可以&#xff0c;我应该怎么做才能恢复数据呢&#xff1f;” 很多笔记本电脑用户可能会不小心地从电脑中…

翻译|解开LLMs的神秘面纱:他们怎么能做没有受过训练的事情?

大语言模型&#xff08;LLMs&#xff09;通过将深度学习技术与强大的计算资源结合起来&#xff0c;正在彻底改变我们与软件互动的方式。 虽然这项技术令人兴奋&#xff0c;但许多人也担忧LLMs可能生成虚假的、过时的或有问题的信息&#xff0c;他们有时甚至会产生令人信服的幻…

顶顶通呼叫中心中间件-打电话没声音检查步骤(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-电话没声音检查步骤(mod_cti基于FreeSWITH) 检查步骤 1、检查配置文件 检查配置文件&#xff1a;打开ccadmin -> 配置文件 -> vars -> external_ip$${local_ip_v4}看一下这个有没有配置正确的外网IP&#xff0c;如果没有配置正确就需要配置正…

方格验证码输入框实现方式

引言 在实际开发过程中验证码输入框是一个很常见UI界面。通常来讲有简单的输入框&#xff0c;也有方格的输入框&#xff0c;其中相对较为棘手就是这种方格输入框里面还需要显示光标的情况。本篇博客我们就来主要讨论一下方格带光标的验证码输入框样式。 实现方案 在着手实现…

顺序结构 ( 六 ) —— 顺序结构实例 【互三互三】

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e;&#x1f680;所属专栏&#xff1a;C教程&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &am…

什么是RLHF(基于人类反馈的强化学习)?

什么是RLHF&#xff08;基于人类反馈的强化学习&#xff09;&#xff1f; 基于人类反馈的强化学习&#xff08;Reinforcement Learning from Human Feedback, RLHF&#xff09;是一种结合强化学习和人类反馈的技术&#xff0c;用于训练智能体&#xff0c;使其行为更符合人类期…

农牧行业CRM洞察:打造营、销、服一体化数字营销平台

01、行业应用背景 保持企业活力&#xff0c;支撑业务单元协调发展&#xff0c;稳定核心产品竞争力&#xff0c;将成为农牧行业企业数字化、数智化建设的指导方向。 积极发挥数据在生产、流通、消费各个环节的决策支撑&#xff0c;为农牧企业特别是多业态集团型企业&#xff0…

1.浅谈蓝牙BLE的总体框架

这里只展开BLE这一部分&#xff0c; 框图如下所示 蓝牙也是使用分层的结构组织代码。 Application&#xff1a;是自己的业务逻辑实现的地方。当然应用程序需要根据BLE的规定&#xff0c;实现配置文件&#xff08;profile&#xff09;、服务&#xff08;service&#xff09;和…

【话题】开源项目:从边缘到主流的转变之旅

目录 开源项目有哪些机遇与挑战&#xff1f; 前言 宏观视角&#xff1a;开源项目的发展趋势 开源运动&#xff0c;作为一股不可忽视的创新力量&#xff0c;正在重塑全球科技版图。其核心价值在于打破知识的壁垒&#xff0c;推动技术的民主化&#xff0c;让信息与技术不再为少…

C++ :内联函数inline|nullptr

欢迎来到HarperLee的学习笔记&#xff01; 博主主页传送门&#xff1a;HarperLee博客主页&#xff01; 欢迎交流学习&#xff01; 一、inline关键字 1.1 什么是内联函数&#xff1f; 内联函数&#xff1a;用** inline 修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用…

PostgreSQL行级安全策略探究

前言 最近和朋友讨论oracle行级安全策略(VPD)时&#xff0c;查看了下官方文档&#xff0c;看起来VPD的原理是针对应用了Oracle行级安全策略的表、视图或同义词发出的 SQL 语句动态添加where子句。通俗理解就是将行级安全策略动态添加为where 条件。那么PG中的行级安全策略是怎…

R包:‘ggcharts好看线图包‘

介绍 ggcharts提供了一个高级{ggplot2}接口&#xff0c;用于创建通用图表。它的目标既简单又雄心勃勃:让您更快地从数据可视化的想法到实际的绘图。所以如何?通过处理大量的数据预处理&#xff0c;为您模糊{ggplot2}细节和绘图样式。生成的图是ggplot对象&#xff0c;可以使用…

CTF php RCE(三)

0x07 日志文件包含 判断类型 使用kali curl -I urlF12 打开F12开发者工具&#xff0c;选中之后F5刷新查看server类型即可 配置文件 直接包含或者访问如果有回显就是&#xff0c; NGINX&#xff1a;NGINX 的配置文件通常位于 /etc/nginx/ 目录下&#xff0c;具体的网站配…

【深度学习入门篇 ④ 】Pytorch实现手写数字识别

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

LLMs可以进行任务规划吗?如果不行,LLMs+GNN可以吗?

深度图学习与大模型LLM(小编): 大家好,今天向大家介绍一篇最新发布的研究论文&#xff08;20240530&#xff09;。这篇论文探讨了如何通过引入GNN来提高大模型在任务规划(task planning)中的性能。*论文分析了LLMs在任务规划上的局限性,并提出了一种简单而有效的解决方案。* 1.…

VIM模式之间的切换

命令行界面下&#xff0c;常用的文本编辑器是 VI / VIM(VI增强版)&#xff0c;VI 是 Linux 最通用的文本编辑器&#xff0c;VIM相较于VI&#xff0c;提供了代码高亮等功能&#xff0c;两者用法完全兼容&#xff1b; 1. 进入 VIM 工作界面 vim 文件名 2. 进入编辑模式 三种方…

深入分析与解决4.3问题:iOS应用版本更新审核被拒原因解析

深入分析与解决4.3问题&#xff1a;iOS应用版本更新审核被拒原因解析 在iOS应用开发和发布过程中&#xff0c;遇到4.3问题&#xff08;设计 - 垃圾邮件&#xff09;是一个常见且令人头疼的情况。即使您的应用已成功发布其第一个版本&#xff0c;但在进行版本更新时&#xff0c…

【React Hooks原理 - useState】

概述 useState赋予了Function Component状态管理的能力&#xff0c;可以让你在不编写 class 的情况下使用 state 。其本质上就是一类特殊的函数&#xff0c;它们约定以 use 开头。本文从源码出发&#xff0c;一步一步看看useState是如何实现以及工作的。 基础使用 function …

数据结构day6链式队列

主程序 #include "fun.h" int main(int argc, const char *argv[]) { que_p Qcreate(); enqueue(Q,10); enqueue(Q,20); enqueue(Q,30); enqueue(Q,40); enqueue(Q,50); show_que(Q); dequeue(Q); show_que(Q); printf(&qu…

小程序复制功能不可用 setClipboardData:fail no permission

先上图 用户协议剪切板也更新但是依旧报错了 最后在公众平台通知里发现是用户之前小程序有规格被封禁了该功能