String类
在C语言中,我们对于对于字符数组的认识,仅仅局限于字符串,我们对于该数组进行一些操作的时候,往往要配合str系列的库函数来使用,但是这些库函数比较繁琐,底层空间需要用户来维护,有可能会在操作中越界访问。
C++中提供了string类,来完善对于字符串的使用和处理
总结:
- string类型是表示字符串的字符串类
- 该类的接口与常规的容器的接口基本相同,再向其中添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
- 不能操作多字节或者变长字符的序列。
String的用法
我们对于String的使用从string的构造开始
string类对象的构造方法
四种构造方法:
1. string( )空参构造,得到一个空的字符串类型
2.string(const char* s) 使用C-string来构造string类对象,即传参一个字符串"xxx"
3.string(size_t n ,char c)表示得到一个n个c字符的字符串
4.string(const string& s)表示通过拷贝构造函数来得到新的string对象
下面是代码演示:
int main() { string s1; //得到空字符串 string s2("hello world!!!"); //字符串内容为hello world!!!\0 string s3(s2); //拷贝构造函数,数值一样,地址不同 string s4(10,'c');//十个字符为c的连起来的字符串 return 0; }
string类的容量操作
函数名称 | 功能说明 |
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串是否为空串,如果为空,返回true,反之返回false |
clear | 清空字符串 |
reserve | 扩容,为字符串预留足够的空间 |
resize | 设置有效字符的个数,多出来的空间用‘\0’来代替 |
代码演示
size和length的区别
size()和length()方法底层实现的原理是一样的,只是为了与其他STL容器接口保持一致,才又加入了size(),以后一般情况下,直接使用size()即可
#include<iostream> #include<string> using namespace std; //需要导入这些头文件以及展开std命名空间,下文不再一一写入代码段 void test11() { string s("hello world"); cout << s.size() << endl; //11 cout << s.length() << endl;//11 } int main() { test11(); return 0; }
capacity的使用
capacity在不同的环境下的初始化的数值是不同的,在VS下我们根据下面代码可知,初始值为15,后序大概为1.5倍扩容,在Linux下初始值从0开始,。后序大概为2倍扩容。
void test12() { //测试capacity String::string s; int num = s.capacity(); cout << num << endl; for (int i = 0; i < 100; i++) { s += i; if (num != s.capacity()) { cout << "capacity改为:" << s.capacity() << endl; } num = s.capacity(); } } int main() { test12(); return 0; }
empty和clear的使用
empty是用来判断当前字符串是否为空值(空串),真返回1,假返回0
clear用来清理字符串内容的,实际上是通过'\0'来实现的,会改变size的数值为0,capacity不会改变
void test13() { string s("hello world"); //检验empty的使用 cout << s.empty() << endl; s.clear(); //当清空clear字符串s之后,再通过empty判空 cout << s.empty() << endl; //empty返回为0表示假,返回为1表示真 //判断clear之后是否size以及capacity发生改变 string s1("hello world"); cout << "当前capacity为:"<<s1.capacity() << endl; cout << "当前size为:" <<s1.size() << endl; //清理clear s1.clear(); cout << "当前capacity为:" << s1.capacity() << endl; cout << "当前size为:" << s1.size() << endl; } int main() { test13(); return 0; }
reserve和resize的使用
reserve是用来扩容更改capacity的,reserve在VS中只能向上扩容,不能缩容(减小),且不会改变size的大小
resize是用来改变字符串长度大小的,不管如何要满足size<=capacity,所以resize底层会在更改size大小前调用reserve来判断此时是否需要扩容,所以可能会影响capacity的大小
//capacity的大小只能是在15 31 ...这样的取值,保证大概1.5倍扩容,向上扩容 void test14() { string s("hello world"); cout << "原始数值" << endl; cout << "当前capacity为:" << s.capacity() << endl; cout << "当前size为:" << s.size() << endl; //验证reserve和resize s.reserve(20); cout << "使用reserve扩容后:" << endl; cout << "当前capacity为:" << s.capacity() << endl; cout << "当前size为:" << s.size() << endl; s.resize(20); cout << "使用resize更改size后:" << endl; cout << "当前capacity为:" << s.capacity() << endl; cout << "当前size为:" << s.size() << endl; cout << endl; //如果此时我们通过reserve缩容(减小当前容量) s.reserve(10000); cout << "缩容为10:结果如下" << endl; cout << "当前capacity为:" << s.capacity() << endl; cout << "当前size为:" << s.size() << endl; //那么我们继续缩小size s.resize(100); cout << "缩小size为5:结果如下" << endl; cout << "当前capacity为:" << s.capacity() << endl; cout << "当前size为:" << s.size() << endl; } int main() { test14(); return 0; }
shrink_to_fit()
一般capacity扩容之后,不会再缩容,但是通过shrink_to_fit将容器的内部存储空间缩小到恰好容纳当前元素数量,以节省不必要的内存空间
shrink_to_fit使得capacity==size
总结
- size()与length()底层实现是一样的,是因为为了与STL其他容器兼容,所以增加了size()的使用,以后一般使用size()
- clear()只是将string中有效字符清空,不会改变底层空间的大小
- resize(size_t n) 和resize(size_t n , char c) 都是可以改变size,只是对于多余的空间,前者默认用‘\0’来填充,后者用指定字符c来填充,其他是一致的。
- resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。也就是size<=capacity
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
string类对象的访问
我们有四种方法来对于string对象进行访问
函数名称 | 功能说明 |
operator[ ] | 返回指定位置的字符,const string类对象调用 |
begin + end | begin( )获取string对象的首元素的迭代器+end( )获得最后一个字符的下一个位置的迭代器 |
rbegin+rend | rbegin得到最后一个字符的迭代器+rend得到string对象第一个字符的上一个位置的迭代器 |
范围for(增强for) | 是C++11支持的for循环遍历方式,本质是调用迭代器 |
void test15() { string s("hello world"); //1.operator[] 访问,可以赋值 和数组一样 cout << s[0] << endl; //2.begin 和 end 的使用 迭代器类似于指针的使用 string::iterator it = s.begin(); //auto it =s.begin(); 可以用auto智能指针接收 while(it!=s.end()) { cout << *it << ""; ++it; } cout << endl; //3.rbegin和end的使用,就是反向遍历 string::reverse_iterator rit = s.rbegin(); while (rit != s.rend()) { cout << *rit << ""; ++rit; //反向遍历也是要++rit迭代器 } cout << endl; //4.范围for for (auto ch : s) { cout << ch << ""; //范围for只能正向遍历 } cout << endl; } int main() { test15(); return 0; }
注意:反向迭代器和正向迭代器的rit/it都是++
string类对象的修改操作
函数名称 | 功能说明 |
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串str |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式的字符串,也就是const char* (char指针) |
find+npos | 从字符串pos位置开始向后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始向前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
insert | 在pos指定位置插入字符串str,很多insert用法,常用的如下。 |
earse | 在pos位置开始,删除n个字符 |
函数的使用
push_back和append以及operator+=
都是在string类对象尾部,添加字符或者字符串,push_back添加字符C,append以及operator+=添加字符串
void test16() { string s;//空串 //push_back的使用 s.push_back('1'); s.push_back('2'); s.push_back('3'); s.push_back('4'); s.push_back('5'); for (auto ch : s) { cout << ch << " "; } cout << endl; //append s.append("hello world"); for (auto ch : s) { cout << ch << " "; } cout << endl; //3.operator+= s += "abcdefg"; for (auto ch : s) { cout << ch << " "; } } int main() { test16(); return 0; }