文章目录Cstring类为什么要学习string类C语言字符串的问题标准库中的string类string类string类的常用接口说VS 和 G 下 string 的结构小试牛刀Cstring类为什么要学习string类C语言字符串的问题C 语言中字符串是以\0结尾的字符数组为了操作方便C语言提供了一些str系列函数例如strlen、strcpy、strcat。但是这些库函数与字符串是分离开的不太符合OOP的思想而且底层空间需要用户自己管理稍不留神可能还会越界访问。标准库中的string类string类string类型文档介绍。string是表示字符串的字符串类该类的接口与常规容器的接口基本相同再添加了一些专门用来操作string的常规操作。string在底层实际是basic_string模板类的别名typedef basic_stringchar, char_traits, allocatorstring;不能操作多字节或者变长字符的序列。在使用string类时必须包含#include头文件以及using std::string;比较不舒服的地方就是string类并不支持char类型的构造原因大致有一下几种C语言的char类型既是字符也是字符串这就导致string准确没办法处理所以就干脆不处理就算处理也是将char类型直接当成字符串来处理因为C标准库强烈倾向于显式优于隐式。这是因为迭代器的原因后续我们会了解我为什么说是因为迭代器的原因这里就简单说一下迭代器可以让这些类容器拥有一套统一的访问接口从而完美地与各种泛型算法因为标准库有 string(size_t n, char c)为了避免产生逻辑冲突那么可以认为2和3才是为什么C不提供char类型构造的核心原因。其实还有一些原因就不展开的说了因为在后续我们对C的不断的学习就会不断的了解这些原因。string类的常用接口说构造函数函数功能string()构造空 stringstring(const char* s)用 C 字符串构造string(size_t n, char c)包含 n 个字符 cstring(const string s)拷贝构造#includeiostream#includestringusingstd::string;voidtest1(){string s1;// 构造空的string类对象s1strings2(hello world);// 用C-string来构造string类对象s2strings3(s2);// 拷贝构造s3strings4(5,x);// 构造包含 n 个字符 c类对象s4}string类对象的容量操作函数功能size()返回有效字符长度length()同上历史原因保留capacity()返回空间总大小empty()是否为空clear()清空有效字符reserve()预留空间resize()改变有效字符个数voidtest2(){string s1hello world;intsizes1.size();coutsize: sizeendl;sizes1.capacity();coutcapacity: sizeendl;if(s1.empty()){couts1为空!endl;}else{couts1不为空!endl;}s1.clear();couts1endl;strings2(hello c);s2.reserve(13);couts2.size()endl;s2.resize(12,a);couts2endl;}注意size() 和 length() 底层一致推荐 size()为了和其他容器统一。clear() 只清空有效字符不改变底层空间大小。resize(n) 增大时用 ‘\0’ 填充resize(n, c) 用 c 填充。resize 增大可能扩容减小时底层容量通常不变。reserve(n) 只预留空间不改变有效元素个数如果 n 小于当前容量通常不改变容量。string类对象的访问及遍历操作函数功能operator[]返回 pos 位置字符begin()/end()正向迭代器rbegin()/rend()反向迭代器范围 forC11 简洁遍历voidtest3(){string s1abcdefg;// 常规for(size_t i0;is1.size();i){couts1[i] ;}coutendl;// 范围forfor(autoch:s1){coutch ;}coutendl;}注范围for的底层是迭代器来实现的范围for使用起来更方便并且不易越界。tring类对象的修改操作函数名称功能说明push_back在字符串后尾插字符cappend在字符串后追加一个字符串operator在字符串后追加字符串strc_str返回C格式字符串find npos从字符串pos位置开始往后找字符c返回该字符在字符串中的位置rfind从字符串pos位置开始往前找字符c返回该字符在字符串中的位置substr在str中从pos位置开始截取n个字符然后将其返回voidtest4(){string s1;s1hello;s1 world;couts1endl;// char ch s1;constchar*chs1.c_str();size_t sizes1.find(hello);if(size!string::npos){cout找到了endl;}else{cout没有找到了endl;}couts1.substr(0,5)endl;}注意push_back( c)、append(1, c)、 ‘c’ 效果类似但 最常用。 不仅能连接字符还能连接字符串。如果能预估字符数量先 reserve。string类非成员函数函数功能说明operator尽量少用因为传值返回导致深拷贝效率低operator输入运算符重载operator输出运算符重载getline获取一行字符串relational operators大小比较voidtest5(){string s1;string s2hello ;string s3world;s1s2s3;couts1endl;s1.clear();std::getline(cin,s1);couts1endl;if(s1s2)std::couts1 s2(内容相同)std::endl;if(s1!s2)std::couts1 ! s2std::endl;if(s1s2)std::couts1 s2 (a的ASCII小于b)std::endl;if(s1s2)std::couts1 s2std::endl;}注如果连续输入string类型不要使用cin s1因为它遇到空格就会退出建议使用std::getline(cin,s1);VS 和 G 下 string 的结构VS 下小字符串优化 SSO32 位平台下VS 的 string 大约占 28 字节vs下string的结构string总共占28个字节内部结构稍微复杂一点先是有一个联合体联合体用来定义string中字当字符串长度小于16时使用内部固定的字符数组来存放当字符串长度大于等于16时从堆上开辟空间union_Bxty{// storage for small buffer or pointer to larger onevalue_type _Buf[_BUF_SIZE];pointer _Ptr;char_Alias[_BUF_SIZE];// to permit aliasing}_Bx;这种设计也是有一定道理的大多数情况下字符串的长度都小于16那string对象创建好之后内部已经有了16个字符数组的固定空间不需要通过堆创建效率高。还有 size、capacity、指针等字段。故总共占1644428个字节。g下string的结构G下string是通过写时拷贝实现的string对象总共占4个字节内部只包含了一个指针该指针将来指向一块堆空间内部包含了如下字段空间总大小字符串有效长度引用计数struct_Rep_base{size_type M_length;size_type M_capacity;_Atomic_word M_refcount;};这里的写时拷贝在后续章节我们会了解。写时拷贝就是一种按需修改当你需要拷贝时它才会真的干活如果你不需要它就会在哪里等着你需要就像吃完饭有的是吃完就去洗碗有的是等下次吃的时候再去洗。写时拷贝就是第二种吃的时候在洗碗的表现。小试牛刀我们经过上述对C的string类进行了充分的了解现在我们通过下面的一些编程题巩固一下对string各种接口的使用。仅仅反转字母boolisLetter(charch){if(chAchZ){returntrue;}if(chachz){returntrue;}returnfalse;}stringreverseOnlyLetters(string s){if(s.empty()){returns;}size_t begin0,ends.size()-1;while(beginend){while(beginend!isLetter(s[begin]))begin;while(beginend!isLetter(s[end]))--end;std::swap(s[begin],s[end]);begin;--end;}returns;}核心思路定义两个双指针头和尾跳过非字母交换字母。第一个只出现一次的字符intfirstUniqChar(string s){intbuffer[256]{0};for(autoch:s){buffer[ch];}for(inti0;is.size();i){if(buffer[s[i]]1){returni;}}return-1;}思路统计每个字符出现次数再按原顺序找第一个次数为 1 的字符。为什么用 256 数组字符范围通常是 0~255直接映射计数O(n) 时间实际上不一定就要用256要根据题目给出的范围来定比如保证了题目只有小写字母就是只需要给数组26的大小即可。最后一个单词的长度intmain(){string s1;while(getline(cin,s1)){size_t poss1.rfind( );couts1.size()-pos-1endl;}return0;}我们在上面了解过为什么这里使用getline使用rfind是因为可以直接从后往前找最后一个空格空格后面的部分就是最后一个单词。同样这里的判断条件需要根据题目描述决定。本章节我们了解了C的string类下一章节我们就开始自己去实现属于直接的string类了为什么要实现string类因为我要通过实现string类来了解底层到底是怎么回事从而进一步加强我们对该类的了解。完
