
大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~,这篇文章是衔接上文,因为string类内容较多,我可能会分3-4篇来写,连贯的来看效果更佳。前情回顾:C++ STL string类全面指南:从编码历史到实战应用

max_size接口的作用是返回该string最大有多长,这个长度根据不同平台的值是不同的。我感觉其意义不大,这里32位平台下最大是21亿多字节(已经2个G了),但实际上string是开不出这么大的连续空间的。前面内存管理的文章,不断地new,最多才开了不到2个G的空间。64位平台下的值更大,换个编译器这个值还有可能不同

capacity就是容量,表示能存储的实际有效字符个数。注意,size和capacity都不包含’\0’,这里显示空间大小为15,实际上底层的空间大小是16

再看一下底层,就是下面的原始视图,数据就存在_Buf中,实际上就是16个空间

clear就是把数据清到0,'\0’会移到前面,但它不会清空间,size变为0,capacity不会变



empty就是判空
Modifiers就是修改

上面就是尾插和扩容,这个扩容机制有些奇怪。在VS2022的C++编译器中,扩容的机制:第一次是二倍(第一次的空间是16字节,因为没有算入’\0’,但是’\0’也有存储空间),其余都是1.5倍(取整了)
补充:STL的设计是一种规范,规定哪些容器和算法,要实现哪些接口,不同的编程平台实现是不一样的。例如扩容的机制,G++是二倍扩容,并且扩容时机不同,也就是写程序时不能依赖某个平台下底层的扩容机制(或某个机制),不然容易出问题

reserve是保留的意思,要和反向迭代器区分开来(reverse 颠倒,反转)

这种情况就是变得比需求更大,容量从15变到31,G++下会从15变为30

如果容量缩小,capacity有可能会变,有可能不会,但肯定不会缩容为5,因为该接口要求不能对内容有影响,该处内容长度都为11字节,缩容最多缩减到11字节,VS2022下没有影响容量。G++下会从30缩容为15
reserve主要应用于下面的场景,知道要插入200个字符,就可以提前开空间。这样插入的过程中就不用扩容了(扩容的代价是很大的,如果一个空间后面没有足够的空间原地扩容,就要异地开空间,拷贝数据,释放原空间),就提高了效率

给了200,扩容到207

缩容使用该接口即可,该接口也不一定会缩到和size一样大

缩容的原理:现在有一块32个字节大小的空间,在前面放了11-12个字符,由于C/C++是不支持释放一部分空间的,所以缩容只能去开一块更小的空间,把数据拷贝下来后,再把旧空间释放掉。这个过程就是经典的的以时间换空间的逻辑,但是现在的硬件设备都很大,相比起来时间更为宝贵,所以一般情况下不建议缩容


resize是对size的一个改变,就不涉及容量问题了,而是改变其中的数据,图中值初始化的字符就是’\0’
插入了字符,但是看不到,‘\0’这个字符打印不出来,要从监视窗口看底层

这里有两个’\0’,实际上只有一个,另一个是之前删除时遗留下来的

所以resize既可以插入,也可以删除,甚至给的空间很大还可以扩容

用于插入多个字符,意思是追加

这里append实现了很多个版本,但是最常用的是第三个版本

再展示一下第六个迭代器区间的用法,插入s2的字符串不想要第一个空格和感叹号,就可以使用迭代器区间



+=的优势就是直接把字符串拼接到一起,不用考虑空间,C++这里就比C语言爽多了,C语言虽然也有和C++STL类似的str系列的库,里面有接口也可以追加字符串,但是并不关心底层的空间,在追加之前要把空间提前开好,空间不够要扩容时无法解决,就设计的不够好
+=是返回一个string的引用,就是返回自己,+没有重载为成员函数,而是重载为非成员函数(也就是全局的),+是以传值的方式返回,也就是不改变自身



比较大小也是如此(重载为全局函数),比较大小是按ASCII码来比较的

assign与赋值有一些重叠,不过更加多元化了


string没有实现头插头删,因为头插头删的效率低,既不提供也不希望使用。而insert和erase可以实现头插头删的方式。尾插和尾删是实现了

insert依旧实现了很多版本,其实这也是一种历史原因,string由于在STL之前就设计了,设计的很早就没有参考,所以有些接口设计的很杂很乱,有些接口冗余了。这里最常用的是第三个版本

erase可以删除一个迭代器位置

这里的npos和前面的逻辑一样,如果字符串太短,给的len超过了字符串长度,有多少删多少,如果给npos,就删除到结尾


依旧有很多版本,大致就是进行一部分的替换,pos和len指定哪一部分的替换

第一个替换就是插入了,插入就要挪动数据,replace少量的使用是没事的,不能大量的使用 第二个替换就要把world向前挪两个位置,再将%改为*


在 C++ 编程中,经常会遇到需要与 C 语言接口交互的情况,尤其是在文件操作、网络编程或数据库访问等场景中。这时,std::string 类的 c_str() 和 data() 方法就显得尤为重要。
这两个方法都返回指向 std::string 内部字符数组的指针,类型为 const char* 。它们的共同点是:都返回一个指向字符串数据的常量指针;在 C++11 及以后的标准中,data() 也保证以空字符 \0 结尾,与 c_str() 行为一致。 注意:在 C++11 之前,data() 不保证以 \0 结尾,而 c_str() 总是保证。
之所以需要 c_str()的原因是C 语言的很多函数(如文件操作、网络通信、数据库接口等)只接受 const char* 类型的参数,而不支持 C++ 的 std::string。这时,我们就需要将 std::string 转换为 C 风格的字符串。
#include <iostream>
#include <string>
#include <cstdio> // 用于 FILE*, fopen
int main() {
std::string filename("Test.cpp");
FILE* fout = fopen(filename.c_str(), "r"); // 必须使用 c_str()
if (fout) {
std::cout << "打开文件成功" << std::endl;
fclose(fout);
} else {
std::cout << "打开文件失败" << std::endl;
}
return 0;
}在这个例子中:fopen 是 C 标准库函数,它需要 const char* 类型的文件名;我们使用 filename.c_str() 将 std::string 转换为 C 风格字符串;这样才能成功调用 C 语言接口。
C++中调用c的接口就很多场景,例如一些库提供API(接口)的时候会按C的方式提供,或在Linux下调网络,线程相关的接口。包括MySQL数据库,它提供C/C++的接口都是以C的方式提供,以C的方式提供,C语言可以用,C++也可以用,很省事。也就是说虽然当前程序是用C++写的,必然有时候还要调用C风格的接口
重要:返回的指针是只读的:c_str() 返回的是 const char* ,不能通过该指针修改字符串
get_allocator就是获取内部用的内存池 STL中的容器底层申请内存时为了提高效率不会去直接调用malloc和new,而是调用内存池。allocator就是内存池对象
所有的数据结构都有第三个参数,该参数就是库中写的内存池对应的对象,例如:string中申请内存是通过内存池对象的类型去申请的

该模板参数默认都传缺省参数,默认使用库中的内存池。平时使用这些数据结构时不用关心,就让其使用模板的缺省参数就好

使用缺省参数的核心原因就是,如果你对库中的内存池不满意,可以自己写一个内存池,显式的传到该参数这里
该接口也用的很少

把我当前的一部分子串(pos位置开始的len个字符)拷贝到s指向的数组上,一般会用另一个接口substr

该接口很好用,它不再会把子串拷贝到一个char* s指向的数组之中,而是会构造一个string对象返回 这里缺省参数也给了npos(若给的参数比剩余字符串长,有多少拷贝多少,取到结尾),且其std::string是 C++ 的字符串类,其内部存储并不依赖’\0’(空字符)来标识字符串结束(而是通过记录长度来管理),第一个参数是 “起始索引”,该索引位置的字符会被包含在结果中,第二个参数是 “要提取的字符数量”

这里把网址拆解出来,第一段是协议,第二段是域名(发向的网站的服务器的地址,该地址会通过DNS协议转换为IP),剩下的就是域名之下的内容,以斜杠为分隔符 补充:url就是网址,https/http称为网络协议,有s就是内容在网络中传输会加密,安全性更高

这就是substr的优势了,如果使用copy还要提前算数组有多长,提前开空间,substr底层自己管理很方便



若原串中有和子串有匹配的字符,返回原串该字符的下标,也就是原串中所有的字符都要和字串中的每个字符挨个比对一遍

和前面的功能是一样的,不过是从后向前找


若原串中有和子串有不匹配的字符,返回原串不匹配字符的下标,也就是原串中所有的字符都要和字串中的每个字符挨个比对一遍

find_last_not_of是倒着向前找

比较的时候使用运算符重载,该接口没有重载为成员函数,而是重载为全局函数的原因就是可以支持string和string比较,const字符串和string比较,string和const字符串比较等等,重载为成员函数,第一个参数就是隐含的this指针,传参的时候第一个参数一定要传string

str和url比较,不可以直接写为str<url,会存在运算符优先级问题,因为<<也是一个运算符,其优先级高于<。(cout<<str)会把str输出到流对象之中,流对象会返回一个ostream类型的对象,ostream类型的对象面对小于符号无法解析,编译就会报错
比较大小是按ASCII码比较的,图中就是h和P来比较,P比h小(ASCII码表中大写的字母在前,小写的字母在后),ASCII码小的就小,ASCII码相等再去比较下一个字符
重载的+=是成员函数,+是不是成员函数

这样设计的原因也和前面的relatonal operators一样


用+需要谨慎,substr和+是传值返回(返回的是返回对象的拷贝),代价更大一些。但现在的编译器依旧优化的很好了,在C++11之后,传值返回优化的依旧效率很高了,如果用拷贝的方式接收,几乎达到不拷贝的程度,这部分我在类和对象下写过
string ret = str + "xxxx";优化时,str + “xxxx”;的返回值会直接成为ret的别名,但是这种优化只有在新的编译器上才有的,这种优化能力C++标准还没有规定,所以还是要注意

该题就可以使用replace将空格替换为%%,首先需要使用find接口找到空格,代码中使用第三个版本,第二个参数默认从0位置开始找

由于返回值是size_t,是无符号整型,找到和没找到就靠npos,npos是静态成员变量,属于string类域

代码中不断地向后寻找空格的逻辑,find的底层就实现了
该方法的缺点就是效率很低,尤其是面对这样一个字符串
string s4("hello world hello yunze");一个空格替换为两个百分号,后面的内容就要挪动一次,每次替换都意味着大量的挪动,挪动又是循环,空格多的时候有很大的性能浪费
下面是一个新的方法

该方法将所有的空格替换为了%,但是完全没有挪动数据 由于+=就是插入,空间不够是要扩容的,reserve这句的作用是提前扩容,如果串很长,可以一定程度上减少扩容。更精准的扩容就是把空格数计算出来也加进去