欢迎来到CILMY23的博客
🏆本篇主题为:深入探索C++之std::string:不止于字符串
🏆个人主页:CILMY23-优快云博客
🏆系列专栏:Python | C++ | C语言 | 数据结构与算法 | 贪心算法 | Linux
🏆感谢观看,支持的可以给个一键三连,点赞关注+收藏。
✨写在前头:
了解完模板和STL后,我们要开始研究std命名空间中的string,作为我们STL学习的开端,string对我们重新认识C语言中的字符串有很大意义。在上文中我们说过容器(Containers),容器是用来存储数据的对象,如数组、链表、树结构等。STL提供了多种类型的容器,本期我们将了解string类,虽然string并没有被归为容器,但它也和容器类似。
目录
🎈size(⭐),length,capacity,max_size
string
一、string的前瞻
1.1 C语言中的字符串
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
我写C语言字符串系列篇的时候还是深有体会的,稍不留神就越界访问导致出错,有时候还难排查出来。
1.2 string的基本概念
string 类是 C++ 标准库中的一部分,它提供了对字符序列的封装,使得字符序列的操作变得既简单又安全。
☄️☄️文档阅读
我们来看看文档是怎么说的:文档的阅读网站我放在了文章末尾,在这里我分了两部分的文档基本阅读
🍀🍀图一:
🍀🍀图二:
☄️☄️信息翻译
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
☄️☄️总结
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
4. 不能操作多字节或者变长字符的序列。
⚠⚠ 在使用string类时,必须包含 #include 头文件以及 using namespace std;
二、string类的常用接口
💫String 类的默认成员函数
string的默认成员函数我们在这里主要看以下三部分:
🍃构造函数(⭐)
进入构造函数的文档界面,我们看到有七个构造函数,其中重点掌握的,也是最常用的,我在图片中已经标出来了。剩下的了解就差不多了。
在这里我们要了解第三个,前面两个很容易理解,但是第三个是什么呢?npos又是什么呢?
我们来看声明和定义,文档在底下给了我们的定义
string (const string& str, size_t pos, size_t len = npos)
解析我在图片中标出来了,大家可以研究研究。
substring constructor是指字串构造。
现在主要的问题是 npos 是个什么东西? 文档给我们提供了链接,我们可以点击查看(链接)
这里一看我们就知道了,原来 npos 是 size_t (无符号整数)的最大值,这就涉及原码反码和补码了(不懂的小伙伴可以看看链接)。
-1 的补码是32个1,也就是2的32次方-1,但是string构造函数并不会开到这么多空间,所以就只会判定拷贝直到str的末尾。
🍭🍭实际操作
写了这么多,我们来实际操作一下。在vs 2019上对这几个构造函数进行使用:记得包含头文件<string>
string s0;
string s1("CILMY23");
string s2(s1,2,3);
string s3(s2);//拷贝构造
string s4(s1, 3, string::npos);//从第三个位置开始,拷贝到str字符串末尾
string s5(10, '*');
string s6("CILMY23", 2);
结果如下:
🍃析构函数
对析构函数我们不必关注太多,稍微看看就行
在学习的时候,我们知道析构函数的功能即可
🍃赋值运算符重载
在这里赋值运算符重载主要给了三种方式
这三种方式的实操如下:
🍭🍭实际操作
string s7;
s7 = s2; //将s2拷贝给s7
s7 = "CILNY23";//将常量字符串赋值给s7
s7 = '*';//将单字符*赋值给s7
结果如下:
赋值运算符重载用的最多的还是前两种,一般第三种还是挺少用的。
💫String 类的容量操作
string 的容量操作一共有以下操作:
我们重点学习:size,empty,clear,reserve,resize
🎈size(⭐),length,capacity,max_size
在string类中,size和length是一样的,都是返回字符串的长度。,它们表示的是字符串中,有效字符的个数,而capacity表示的是容量,max_size,它返回 string 类型对象最多包含的字符数。也就是string类型支持的最大字符数,超过这个数,将无法支持,编译器会拋出 length_error 异常。
我们知道在字符串的末尾是有\0的,在这里是不计入size和length的。
为什么这里会相同呢?
这就不得不提及STL和String的先后问题了,其实是String比STL先出现的,所以String在刚刚的网站中我们是在Container中是找不到String的,但是它跟容器差不多,所以就增加了一个size接口,起初最先我们使用的是length,size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
说完了size和length,我们接下来看看capacity
capacity返回的是开辟空间的大小,它是一个const成员函数,this指针指向的内容是不可以改变的 ,它和size是不一样的,有可能等于size,也可能比size还多
我们可以看一下在vs上它是如何扩容的,
size_t sz = s1.capacity();
int n;
for (n = 0; n < 1000; n++)
{
s1.push_back('*');
if (sz != s1.capacity())
{
sz = s1.capacity();
cout << "capacity changed->" << s1.capacity() << endl;
}
}
结果如下:
我们可以看到它的扩容机制大概是按1.5倍来扩容的
而g++底下的扩容机制是按照两倍扩容的。
所以说具体的这些细节,是不确定的。
🍭🍭实际操作
string s1("hello CILMY23");
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;
cout << s1.max_size() << endl;
cout << s1.npos << endl;
结果如下:
但是我这里max_size可能是受到了系统或者编译器的限制,在我查阅很多文档后,都说它比npos还小1个,这里得考虑字符'\0'。所以最大长度是不