前言
C语言我们学习了字符串和字符串的相关函数,在C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。今天我们将正式步入C++ string类的学习。
1.C++ string类
C++中的string
类是STL的一个重要组成部分,它提供了对字符串的封装和处理功能。
在使用string类时,必须包含#include头文件以及using namespace std;
下面是参考的官方链接
http://www.cplusplus.com/reference/string/string/?kw=string
接下来,我们将进一步学习string类。
2.string类中的常见构造
#include <iostream> #include <string> using namespace std; int main() { string s1; string s2("hello world"); string s3(6, 'x'); string s4(s3); cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s4 << endl; return 0; }
3.string类对象的容量操作
函数名称功能说明
size(重点)返回字符串有效字符长度
length 返回字符串有效字符长度
capacity返回空间总大
void string1() { string s("I love you!!!"); cout << s.size() << endl; cout << s.length() << endl; cout << s.capacity() << endl; cout << s << endl; }
在C++的string
类中,size()
和length()
成员函数返回的是字符串中字符的数量,不包括结尾的空字符(\0
),因为string
类内部管理内存时会自动在字符串末尾添加一个空字符,但这个空字符不计入字符串的长度。
capacity()
成员函数返回的是字符串当前分配的内存能够容纳的字符数量,这个值通常大于或等于size()
的返回值,以容纳未来可能的字符添加操作,而不会立即触发重新分配内存。
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间**
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充
void string1() { string s("I love you!!!"); cout << s.size() << endl; cout << s.length() << endl; cout << s.capacity() << endl; cout << s << endl; // 使用empty()检查字符串是否为空 if (s.empty()) { cout << "s is empty." << endl; } else { cout << "s is not empty." << endl; } //s.clear(); //cout << s.size() << endl; ///cout << s.capacity() << endl; //cout << s << endl; s.resize(18, 'a'); //s.resize(6); cout << s.size() << endl; cout << s.capacity() << endl; cout << s << endl; }
可根据自己的需求自行更改代码。不难发现capacity 的大小会根据字符串的大小变化可能进行相应的变化,变化情况见上capacity的介绍。linux下gcc和VS的capacity变化规则是略有不同的,大家可以下去尝试下。
void string2() { string s; // 测试reserve是否会改变string中有效元素个数 s.reserve(100); cout << s.size() << endl; cout << s.capacity() << endl; // 测试reserve参数小于string的底层空间大小时,是否会将空间缩小 s.reserve(50); cout << s.size() << endl; cout << s.capacity() << endl; //输出原来值或更大,不会缩小 }
void TestPushBack() { string s; size_t sz = s.capacity(); cout << "capacity: " << sz << '\n'; cout << "making s grow:\n"; for (int i = 0; i < 100; ++i) { s.push_back('c'); if (sz != s.capacity()) { sz = s.capacity(); cout << "capacity changed: " << sz << '\n'; } } }
当把\0加上时,我们会发现刚开始的变化是2倍,后面是1.5倍,这是VS自己规定的变化规则,刚开始会默认开设一个大小为16字节的数组,后面变化的空间是在堆上的。
而gcc运行此代码都是2倍的变化。
注意:
1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接 口保持一致,一般情况下基本都是用size()。
2. clear()只是将string中有效字符清空,不改变底层空间大小。
3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
vs和g++下string结构的说明(了解),小编也没有理解的通透。
注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string中字符串的存储空间:
当字符串长度小于16时,使用内部固定的字符数组来存放。 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty { // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE];
// to permit aliasing } _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建 好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量 最后:还有一个指针做一些其他事情。 故总共占16+4+4+4=28个字节。
g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个 指针,该指针将来指向一块堆空间,内部包含了如下字段:
空间总大小 字符串有效长度 引用计数
指向堆空间的指针,用来存储字符串
深度剖析C++string(上篇)(2):https://developer.aliyun.com/article/1624973