《Effective STL》读书笔记(二):vector和string

news2024/9/24 9:23:08

vector 和 string 优先于动态分配数组

当使用new动态分配内存时,我们需要关注以下内容

  1. 必须保证动态分配的内存会被delete,否则会造成资源泄露
  2. 必须确保使用了正确的delete形式。如果分配了单个对象,则必须使用delete;如果分配了数组,则需要使用delete[]。如果使用了不正确的delete形式,结果将是不确定的。可能会导致程序运行时崩溃,也有可能是资源泄露
  3. 必须确保只delete了一次。如果多次delete,结果同样是不确定的

vectorstring就消除了上述的负担,当元素加入到容器中时,它们的内存会增加;当vectorstring被析构时,它们的析构函数会自动析构容器内的元素。

许多string实现背后使用了引用计数技术,可以消除不必要的内存分配和不必要的字符拷贝,但是这种优化在多线程环境下可能会适得其反,所以如果认为string的引用计数实现会影响效率,那么可能有下面这几种选择

  • 看是否可以禁止引用计数
  • 寻找另外一个不使用引用计数的string实现
  • 考虑使用vector<char>来替代stringvector的实现不使用引用计数,不会发生隐藏的多线程性能问题。string的大多数功能可以通过算法库中的函数来替代。

使用reserve来避免不需要的内存分配

如果确切知道容器内最后会有多少元素的话,可以直接让容器预留合适的容量;如果不知道容器内最后有多少元素的话,可以先预留足够大的空间,然后当把所有数据都加入后,再去除多余的容量。

string实现的多样性

几乎每个string实现都包含以下信息

  • 字符串的大小,即包含字符的个数
  • 该字符串的最大容量
  • 字符串的值,即构成该字符串的字符

除此之外还可能包含

  • 它的分配器的一份拷贝

建立在引用计数基础上的string实现可能还包含

  • 对值的引用计数

不同的实现方式之间string的差别很大:

  • string的值可能被引用计数,也可能不会
  • string对象大小的范围可能是一个char*指针的大小的1倍到7倍
  • 创建一个新的字符串值可能需要零次、一次或两次动态分配内存
  • string对象可能共享,也可能不共享其大小和容量信息
  • string可能支持,也可能不支持针对单个对象的内存分配器
  • 不同的实现对字符内容的最小分配单位有不同策略

vector和string数据传给旧的API

要把vector传给使用数组的函数,只需要传入&v[0]即可,vector内部的元素布局保证和数组相同,所以这样的做法是正确的。可能出现问题的地方是如果vector此时的size为0,那么就可能产生不可预知的后果。所以在传参之前需要先判断以下vector.empty()

上面这种方法对string就无效了,因为:

  1. string中数据不一定存储在连续的内存空间中
  2. string的内部表示不一定是以空字符结尾的

所以通常我们使用c_str函数来获取可供C语言使用的字符串指针,即使长度为0也可以。

上面的方法对于要传入const指针的情况是没有问题的,但是如果在函数中要修改vector或者string的值就可能会出现问题。

对于string来说,c_str()返回的并不一定是字符串数据的内部表示,还可能是一个字符串数据的不可修改的拷贝。

对于vector来说,修改其中元素的值通常是没有问题的,但是不能试图修改元素的个数:这会导致vector内部的状态混乱,如果此时vector.size() == vector.capicity(),那么添加新的元素也会产生不可预知的后果。

如果想使用C API来初始化一个vector,那么可以利用vector和数组的内存布局兼容性,向API传入该vector中元素的存储区域

size_t fillArray(double* pArray, size_t arraySize);  // 返回已被写入的double数据的个数

vector<double> vd(maxNumDoubles);		// 创建大小为maxNumDoubles的vector
vd.resize(fillArray(&vd[0], vd.size()));

这样的方法只对vector有效,因为只有vector才保证和数组有同样的内存布局。如果想要使用来自C API的数据初始化一个string,也可以使用其他方法做到。只需要让API把数据放到一个vector<char>中,然后再把数据从vector拷贝到相应的字符串中即可:

size_t fillString(char* pArray, size_t arraySize);

vector<char> vc(maxNumChars);
size_t charsWritten = fillString(&vc[0], vc.size(0));  // 使用fillString向vc中写入数据
string s(vc.begin(), vc.begin() + charsWritten);  // 通过区间构造函数完成

按照这样的方法,也可以初始化其他容器

size_t fillArray(double *pArray, size_t arraySize);
vector<double> vd(maxNumDoubles);
vd.resize(fillArray(&vd[0], vd.size()));

deque<double> d(vd.begin(), vd.end());
list<double> l(vd.begin(), vd.end());
set<double> s(vd.begin(), vd.end());

反过来,其他容器的内容也可以通过vector作为媒介传递到C API中。

void doSomethine(const int* pInts, size_t numInts);
set<int> intSet;

vector<int> v(intSet.begin(), intSet.end());
if (!v.empty()) doSomethine(&v[0], v.size());

使用swap技巧来去除多余的容量

如果一个vector的容量很大,但是其中元素数量比较少,如果我们想把vector的容量缩减到合适的大小(这种容量的缩减通常被称为“shrink to fit”),我们可以通过下面的方法来实现这种缩减

class Contestant{...};
vector<Contestant> contestants;

vector<Contestant>(contestants).swap(contestants);  // shrink to fit

表达式vector<Contestant>(contestants)通过拷贝构造函数创建了一个临时变量,这个临时变量的容量恰好是contestants的元素数量,然后再通过调用swap来交换临时变量和contestants的数据,交换之后临时变量得到了之前臃肿的容量,而contestants的容量大小刚刚好。在这句话结束之后,临时变量析构,多占用的内存就真正得到了释放。

对于字符串这样的操作也同样有效。

需要注意的是,这个技巧并不能完全保证缩减之后的容器一定没有冗余的容量,这是因为可能STL的具体实现会保留一些容量,这是无法避免的,但是这种技巧还是能保证使用后“在容器当前大小确定的情况下,使容量在该实现下变得最小”。

我们还可以通过这种技巧来清空一个容器:

string s;

vector<Contestant>().swap(v);  // 和空容器进行swap
string().swap(s);

避免使用vector<bool>

如果一个对象是STL容器,那么一定下面的条件:如果c是包含对象T的容器,而且c支持operator[],那么下面的代码必须能够被编译

T *p = &c[0];  // 用operator[]返回值的地址初始化一个T的指针

但是这个条件在vector<bool>中是不成立的,像下面的代码无法编译通过

image-20230905181201929

image-20230905181012655

原因是vector<bool>是一个假容器,它并不真的存储bool,为了节省空间它存储的是bool的紧凑表示。在一个典型实现中,存储在vector中的bool仅占一个二进制位,所以一个8位的字节可以容纳8个bool。而指向一个二进制位的指针是被禁止的,所以vector<bool>::operator[]返回的是一个代理对象。

所以最好使用deque<bool>,它里面确实存储的是bool类型的数据,而且相比vector<bool>可以看到的省略只有reservecapacity。另外一个替代方法就是使用bitset,它不是STL容器,但是是C++标准的一部分,但是它的容量在创建时就指定了,没有办法动态调整大小或插入删除元素,因为它不是STL容器,所以它也不提供迭代器,但是提供了很多对位的集合有意义的成员函数。

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

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

相关文章

选择最适合您的Bug管理软件:市场比较与推荐

“Bug管理软件哪家好&#xff1f;市场上有许多优秀的Bug管理系统品牌如&#xff1a;Zoho Projects、JIRA、Redmine、Bugzilla、MantisBT。” 一款高效的Bug管理系统可以帮助团队更有效地发现、记录和解决软件中的问题&#xff0c;从而提高产品质量和用户满意度。本文将为您介绍…

JavaScript基础07——变量拓展-数组

哈喽&#xff0c;大家好&#xff0c;我是雷工! 每天打卡学习一点点&#xff0c;今天继续学习JavaScript基础知识&#xff0c;以下是学习笔记。 一、数组的基本介绍 数组 &#xff08;Array&#xff09;——一种将一组数据存储在单个变量名下的优雅方式。 数组的作用和变量一样…

【电源专题】典型设备的接地设计

在文章:【电源专题】接地的类型 中我们讲到不同的历史时期接地概念是不同的,有为了安全的电气接地和物理接地,也有为了提供参考电位的接地。 那么在设备接地的设计中,我们会怎么进行操作呢? 在文章【电源专题】接地的类型讲到一个混合接地的例子,我们可以把大功率的地接…

DF竞赛平台化身隐私计算大赛“超强助攻”,助力打造专业人才试炼场!

在技术可用性提升、应用需求增强以及政策大力支持的共同作用下&#xff0c;作为保障数据要素安全可信流通的有效技术手段&#xff0c;隐私计算技术火热发展&#xff0c;应用开始逐渐落地。在此背景下&#xff0c;中国信息通信研究院和中国通信学会联合行业多方力量&#xff0c;…

【MySQL】MySQL的安装,登录,配置和相关命令

文章目录 前言一. 卸载不需要的环境二. 获取MySQL的yum源三. 安装MySQL和启动四. 尝试登录MySQL方法1&#xff1a;获取临时root密码方法2&#xff1a;没有密码方法3&#xff1a;配置文件 五. 简单配置结束语 前言 本篇文章是基于云服务器&#xff1b;Linux&#xff1a;Centos7…

【excel密码】excel文件加密方法总结:

想要给Excel文件进行加密&#xff0c;方法有很多&#xff0c;今天分享三种Excel加密方法给大家。 打开密码 设置了打开密码的excel文件&#xff0c;打开文件就会提示输入密码才能打开excel文件&#xff0c;只有输入了正确的密码才能打开并且编辑文件&#xff0c;如果密码错误…

[A题]2023 年全国大学生数学建模比赛思路、代码更新中.....

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Java高并发系列: 使用wait - notify实现高效异步方法

1. 背景 在项目开发中, 通常会有异步执行操作, 例如: 提交一个异步清空一系列数据库中ID ${_id} 的记录, 这个时候通常的做法是主线程将任务添加到一个异步队列中, 后台维护一个线程不断地循环扫描这个队列, 如果有需要执行的任务, 则执行相应的逻辑. 如下图所示: 2. 一个简…

Discourse 附件无法显示的跟进

今天登录表以后&#xff0c;发现数据又被清理了部分。 然后我们又重新使用 SQL 导入了数据。 这个让我们感觉 Discourse 的系统中应该设置了自动清理程序&#xff0c;在这个自动清理程序中会对认为没有使用的附件或者图片进行清理。 因为我们更换了存储空间&#xff0c;所以这…

在Linux中使用shell指令完成文件打包、压缩、解压缩

一、写一个1.sh脚本&#xff0c;将以下内容放到脚本中 在家目录下创建目录文件dir在dir下创建dir1和dir2把当前目录下的所有文件拷贝到dir1中&#xff0c;把当前目录下的所有脚本文件拷贝到dir2中把dir2打包并压缩为dir2.tar.xz再把dir2.tar.xz移动到dir1中解压dir1中的压缩包…

triton 客戶端用https协议访问服务

背景 平时调用模型服务&#xff0c;都是用httpIP的链接调用。但由于笔者环境的特殊性&#xff0c;访问模型必须经过一个https的公网URL&#xff0c;所以&#xff0c;如何用triton client访问https链接成为了一个问题 参考 TensorRT&Triton学习笔记(一)&#xff1a;trito…

win7安装python3.8.10遇到的问题及解决办法和相关资源

在我的另一篇文章中&#xff0c;已经测试了win7安装不了python3.10.8 https://blog.csdn.net/kxltsuperr/article/details/132666737 那么&#xff0c;经过搜索&#xff0c;说win7最高可以安装python3.8.10&#xff0c;于是进行安装&#xff0c;结果报错&#xff0c;如下图&a…

【实战】React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(总结展望篇)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

结构型模式-过滤器模式

允许开发人员使用不同的标准来过滤一组对象&#xff0c;通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式&#xff0c;它结合多个标准来获得单一标准。 AllArgsConstructor Data public class Person {private String name;private String gender;p…

【Three.js】第二十一章 Physics 物理

介绍 物理是WebGL可以添加到项目体验中最酷的功能之一。人们喜欢真实物理感的物体&#xff0c;看到它们碰撞、倒塌、坠落和弹跳&#xff0c;就像我的作品集一样&#xff1a; https: //bruno-simon.com/ 有很多方法可以将物理功能添加到您的项目中&#xff0c;这取决于您想要实…

【脑机接口开源数据处理包】brainflowBrainFlow是一个库,旨在获取,解析和分析脑电图,肌电图,心电图和其他类型的数据从生物传感器。

BrainFlow是一个库&#xff0c;旨在获取&#xff0c;解析和分析脑电图&#xff0c;肌电图&#xff0c;心电图和其他类型的数据从生物传感器。 brainflow开源库官网地址 [https://brainflow.readthedocs.io/en/stable/](https://brainflow.readthedocs.io/en/stable/) 它提供了…

我发现了一个很好看的字体,霞鹜文楷!如何换windows和typora字体?

1、字体 官方地址如下&#xff0c;下载也很简单。 https://github.com/lxgw/LxgwWenKai 有1W多的stars。 方式&#xff1a; 直接打包下载。下载不来&#xff0c;可以联系我。 然后ttf的文件&#xff0c;全部安装就行了。 reg save "HKCU\Control Panel" .\res…

C语言入门Day_16 循环的嵌套

目录 前言 1.循环的嵌套 2.循环和判断相互嵌套 3.易错点 4.思维导图 前言 我们已经知道如何通过循环结构来遍历一个一维数组&#xff0c;访问里面的每一个元素。 我们用循环里面的计数器&#xff0c;来作为数组的下标&#xff0c;就可以简单的遍历数组里面的每一个元素。…

知识蒸馏学习

知识蒸馏----教师和学生模型&#xff1a;将已训练完善的模型作为教师模型&#xff0c;通过控制“温度”从模型的输出结果中“蒸馏”出“知识”用于学生模型的训练&#xff0c;并希望轻量级的学生模型能够学到教师模型的“知识”&#xff0c;达到和教师模型相同的表现。 本质上属…