目录
[1.1 size](#1.1 size)
[1.2 max_size](#1.2 max_size)
[1.3 capacity](#1.3 capacity)
[1.4 clear](#1.4 clear)
[1.5 empty](#1.5 empty)
[2.1 push_back](#2.1 push_back)
[2.2 reserve](#2.2 reserve)
[2.3 shrink_to_fit](#2.3 shrink_to_fit)
[2.4 append](#2.4 append)
[2.5 operator+=与operator+](#2.5 operator+=与operator+)
[2.6 assign](#2.6 assign)
[2.7 insert](#2.7 insert)
[2.8 erase](#2.8 erase)
[2.9 replace](#2.9 replace)
[3.1 c_str 与 data](#3.1 c_str 与 data)
[3.2 get_allocator](#3.2 get_allocator)
[3.3 copy](#3.3 copy)
[3.4 find的四个接口](#3.4 find的四个接口)
[3.5 operator+](#3.5 operator+)
[4.1 用字符替换空格(find)](#4.1 用字符替换空格(find))
一、string的capacity接口
1.1 size

- 返回值 :无符号整数(
size_t类型) - 计数规则
- 只统计有效字符
- 不包含 C 风格字符串末尾的结束符
\0 - 空格、符号、中文都算 1 个字符
- 和
length()完全等价 :str.size()和str.length()功能、返回值一模一样,只是名字不同。
1.2 max_size

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

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

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

二、string的Modifiers接口
Modifiers就是修改
2.1 push_back
尾插


上面就是尾插和扩容,这个扩容机制有些奇怪。在VS2022的C++编译器中,扩容的机制:第一次是二倍(第一次的空间是16字节,因为没有算入'\0',但是'\0'也有存储空间),其余都是1.5倍(取整了)
2.2 reserve
reserve是保留的意思,要和反向迭代器区分开来(reverse 颠倒,反转)

这里需要注意:看似容量为15,31...实则为16,32...,因为这里没有算'\0',若加上'\0'(算一个字符),就为16,32...,所以capacity不计算'\0'。
2.3 shrink_to_fit


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

2.4 append
用于在末尾插入多个字符,意思是追加

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

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


2.5 operator+=与operator+

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




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


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

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

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

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

2.9 replace


三、String的operations接口
3.1 c_str 与 data


在 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 风格的字符串。
cpp
#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* ,不能通过该指针修改字符串
3.2 get_allocator
get_allocator就是获取内部用的内存池
STL中的容器底层申请内存时为了提高效率不会去直接调用malloc和new,而是调用内存池。allocator就是内存池对象
所有的数据结构都有第三个参数,该参数就是库中写的内存池对应的对象,例如:string中申请内存是通过内存池对象的类型去申请的


3.3 copy


3.4 find的四个接口


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

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


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

3.5 operator+



用+需要谨慎,substr和+是传值返回(返回的是返回对象的拷贝),代价更大一些。但现在的编译器依旧优化的很好了,在C++11之后,传值返回优化的依旧效率很高了,如果用拷贝的方式接收,几乎达到不拷贝的程度,这部分我在类和对象下写过
四、string接口的应用
4.1 用字符替换空格(find)


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

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