string类部门(C++)

[复制链接]
发表于 2024-11-27 11:29:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目录
 
1. string类
1.1 auto和范围for
auto关键词:
 范围for:
1.2 string类的常用接口说明
a)string类对象的常见构造
b) string类对象的容量操纵
 size与length:
 capacity:
empty:
clear:
 reserve:
1.reserve(n)中n<>
 2.reserve(n)中n>size而且n<>
 3.reserve(n)中n>capacity时
 resize:
1.resize(n),n:<>
 2.resize(n),n>size而且n:<>
3.resize(n),n>capacity: 
 1.3 string类对象的访问及遍历操纵
operator[]:
 begin+end:
 rbegin+rend:
1.4. string类对象的修改操纵
push_back:
 append:
string& append(const string&str)和string& append(const char*s):
string& append(const string&str,size_t subpos,size_t sublen) 
operator+=:
 c_str:
 find+nops:
size_t find(char c, size_t pos=0) const:
 rfind:
size_t rfind(char c, size_t pos=0) const:
 substr:
 1.5 string类非成员函数
operator>>和operator<<:
 getline:

 
1. string类

在使用string类时,必须包含#include头文件以及using namespace std;
1.1 auto和范围for

auto关键词:

   a)在早期C/C++中auto的寄义是:使用auto修饰的变量,是具有主动存储器的局部变量,后来这个不重要了。C++11中,尺度委员会变废为宝赋予了auto全新的寄义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
  b)用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
  c)当在同一行声明多个变量时,这些变量必须是雷同的类型,否则编译器将会报错,由于编译器现实只对第一个类型进行推导,然后用推导出来的类型界说其他变量。
  d)auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
  e)auto不能直接用来声明数组
  举例b):
  1. int x = 10;
  2. auto y = &x;
  3. auto* z = &x;
  4. auto& m = x;
  5. cout << typeid(x).name() << endl;
  6. cout << typeid(y).name() << endl;
  7. cout << typeid(z).name() << endl;
  8. cout << typeid(m).name() << endl;
复制代码
结果:

   这里auto y=&x;和auto* z=&x;是到达雷同的结果,但是要留意如果是auto*就右边必须是取地点否则就会报错,不能写成auto* y=x,但第二种auto y=&x,就会主动将其辨以为int*类型。
  举例c):
  1. auto aa = 1, bb = 2;
  2. // 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
  3. auto cc = 3, dd = 4.0;
复制代码
  第二种界说就会报错,当cc=3事,编译器就会以为这是int类型,但背面dd不匹配,就会报错。
   举例d):
  1. // 不能做参数
  2. //error C3533: 参数不能为包含“auto”的类型
  3. void func2(auto a)
  4. {
  5. }
复制代码
  当函数中的形参用auto类型就会直接报错。
  举例e):
  1. // 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
  2. auto array[] = { 4, 5, 6 };
复制代码
  这里数组前面界说为auto类型,也会直接报错。
   范围for:

   a)对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会轻易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部门:第一部门是范围内用于迭代的变量,第二部门则表示被迭代的范围,主动迭代,主动取数据,主动判断竣事。
  b)范围for可以作用到数组和容器对象上进行遍历。
  c)范围for的底层很简朴,容器遍历现实就是更换为迭代器,这个从汇编层也可以看到。
   举例:
  1. int main()
  2. {
  3.         int array[] = { 1, 2, 3, 4, 5 };
  4.         // C++98的遍历
  5.         for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
  6.         {
  7.                 array[i] *= 2;
  8.         }
  9.         for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
  10.         {
  11.                 cout << array[i] <<" ";
  12.         }
  13.     cout << endl;
  14.         // C++11的遍历
  15.         for (auto& e : array)
  16.                 e *= 2;
  17.         for (auto e : array)
  18.                 cout << e << " ";
  19.     cout<<endl;
  20.         return 0;
  21. }
复制代码
结果:

1.2 string类的常用接口说明

   a)string类对象的常见构造

  

 举例:
  1. int main()
  2. {
  3.         string s1;                // 构造空的string类对象s1
  4.         string s2("hello world");   // 用C格式字符串构造string类对象s2
  5.         string s3(s2);            // 拷贝构造s3
  6.         string s4(4, 'c');                        //构造n个c
  7.         cout << s1 << endl;
  8.         cout << s2 << endl;
  9.         cout << s3 << endl;
  10.         cout << s4 << endl;
  11. }
复制代码
结果:

   b) string类对象的容量操纵

  函数名称功能说明size返回字符串有用字符长度length返回字符串有用字符长度capacity返回空间总巨细empty检测字符串释放为空串,是返回true,否则返回falseclear清空有用字符reserve为字符串预留空间resize将有用字符的个数该成n个,多出的空间用字符c填充  
 
 
 
 
 
 
 
 
 size与length:
举例:
  1. int main()
  2. {
  3.         string s1("hello world");
  4.         cout << s1.size() << endl;
  5.         cout << s1.length() << endl;
  6. }
复制代码
结果:

   留意:size()与length()方法底层实现原理完全雷同,引入size()的原因是为了与其他容器的接 口保持一致,一样平常环境下根本都是用size()。
   capacity:
举例:
  1. void test1()
  2. {
  3.         string s1("hello world");
  4.         cout << s1.capacity() << endl;
  5. }
复制代码
结果:

这里还要看一下一次开多大的空间,如何开空间的(通过下面代码观察一下每次开多大):
  1. int mian()
  2. {
  3.         string s;
  4.         size_t old = s.capacity();
  5.         cout << "capacity changed: " << old << '\n';
  6.         cout << "making s grow:\n";
  7.         for (int i = 0; i < 1000; ++i)
  8.         {
  9.                 //s.push_back('c');
  10.                 s += 'c';
  11.                 if (old != s.capacity())
  12.                 {
  13.                         old = s.capacity();
  14.                         cout << "capacity changed: " << old << '\n';
  15.                 }
  16.         }
  17. }
复制代码
结果:

   在vs2022编译器下,第一次开发十六个空间(这是15是表示有用存储空间,由于这里还要放“\0”),第一次为2倍,背面都是前面的1.5倍。
  还要留意调试时,观察当字符小于16,当字符大于16。
  1. void test4()
  2. {
  3.         string s1("11111");
  4.         string s2("2222222222222222222222222222");
  5. }
复制代码


   这里发现在vs2022下成员变量中有一个_Buf,就是说当字符数小于16时,就直接存放在_Buf中,避免去频繁调用开发空间,当大于16时,再在_Ptr调用开发空间,将字符存放在_Ptr中。
  empty:
举例:
  1. int main()
  2. {
  3.         string s1("hello world");
  4.         string s2;
  5.         cout << s1.empty() << endl;
  6.         cout << s2.empty() << endl;
  7. }
复制代码
结果:

   为空时,返回1,否则,返回0。
  clear:
举例:
  1. void test2()
  2. {
  3.         string s1("hello world");
  4.         s1.clear();
  5.         cout << s1.size() << endl;
  6.         cout << s1.capacity() << endl;
  7. }
复制代码
结果:

   clear()只是将string中有用字符清空,不改变底层空间巨细。
   reserve:
举例:reserve分为三种环境:
这里先增补一个函数shrink_to_fit()缩容的作用
   1.reserve(n)中n<size
  1. void test5()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.reserve(5);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:

   可以看当n<size时,在vs2022编译器下面reserve并没有缩容减小空间而是保持稳定。
     2.reserve(n)中n>size而且n<capacity
  1. void test5()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.reserve(13);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:

   
可以看到这里很前面的结果一样,在vs2022下只要n是小于capacity时,就不进行缩容,不操纵,保持原来的稳定。

     3.reserve(n)中n>capacity时
  1. void test5()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.reserve(20);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:

   这里发现当n>capacity时,在vs2022编译器下,会开发空间,遵循上面的开发规则,当所需开发的空间不足上面的那一个档位时,(比方:我要开发20个,但根据上面的规则,会直接开发31个,如果要开发32,这个31不够,就会直接开发47个)也会直接开发那个档位所需的空间。
    这里增补一个函数shrink_to_fit:
  1. void test5()
  2. {
  3.         string s("1111111111");
  4.         s.reserve(1000);
  5.         cout << s.size() << endl;
  6.         cout << s.capacity() << endl;
  7.         s.shrink_to_fit();
  8.         cout << s.size() << endl;
  9.         cout << s.capacity() << endl;
  10. }
复制代码
结果:

  这个函数是用来缩容的,但是留意,这个函数是异地缩容,便是重新开发一块缩容巨细之后的空间。(不建议使用)
   resize:
这里同reserve一样分为三种环境:
   1.resize(n),n<size:
  1. void test6()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.resize(5);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:

 

   可以看到当n<size时,会直接多余的(大于n的)删除数据,并将size巨细改为n。
     2.resize(n),n>size而且n<capacity:
  1. void test6()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.resize(13);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:


   可以观察到,这里会将不到n部门的size放置为0,之前原有的数据稳定,并将size巨细改为n。
    3.resize(n),n>capacity: 
  1. void test6()
  2. {
  3.         string s("1111111111");
  4.         cout << s.size() << endl;
  5.         cout << s.capacity() << endl;
  6.         s.resize(20);
  7.         cout << s.size() << endl;
  8.         cout << s.capacity() << endl;
  9. }
复制代码
结果:


    这里可以观察到,当n>capacity时,就会开发新的空间,然后和第二种环境一样,将不到n的部门,补为'\0',之前的部门稳定。
    总结: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不会改变容量巨细。
   1.3 string类对象的访问及遍历操纵

函数名称功能说明operator[]返回pos位置的字符,const string类对象调用begin+endbegin获取一个字符的迭代器 + end获取末了一个字符下一个位 置的迭代器rbegin+rendbegin获取一个字符的迭代器 + end获取末了一个字符下一个位 置的迭代器范围forC++11支持更简洁的范围for的新遍历方式 operator[]:

  1. void test7()
  2. {
  3.         string s("abcdef");
  4.         for(int i=0;i<s.size();i++)
  5.                 cout << s[i] << " ";
  6. }
复制代码
结果:

   这里和C语言的下标访问是一样的,只是这里进行了函数重载,将其封装了一下。
   begin+end:

  1. void test7()
  2. {
  3.     string s("abcdef");
  4.         string::iterator i = s.begin();
  5.         while (i != s.end())
  6.         {
  7.                 cout << *i << " ";
  8.                 i++;
  9.         }
  10. }
复制代码
结果:

   这里的迭代器中的begin(),相称于是指针的用法,但这里不是指针。
   rbegin+rend:

  1. string s("abcdef");
  2. string::reverse_iterator i1 = s.rbegin();
  3. while (i1 != s.rend())
  4. {
  5.         cout << *i1 << " ";
  6.         i1++;
  7. }
复制代码
结果:

   这里就是从末了一个字符开始遍历直到第一个字符,和begin(),end()逻辑是一样的。
  1.4. string类对象的修改操纵

函数名称功能说明push_back在字符串后尾插字符cappend在字符串后追加一个字符串operator+=在字符串后追加字符串strc_str返回C格式字符串find+npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置substr在str中从pos位置开始,截取n个字符,然后将其返回 push_back:

  1. void test8()
  2. {
  3.         string s("abcdef");
  4.         for (int i = 0; i < 5; i++)
  5.         {
  6.                 s.push_back('a');
  7.         }
  8.         cout << s << endl;
  9. }
复制代码
结果:

   直接在字符串末尾加字符c 。
   append:


这里append在类中有六个接口,这里讲几个较常用的:
string& append(const string&str)和string& append(const char*s):
  1. void test8()
  2. {
  3.         string s1("abcdef");
  4.         string s2("hijklm");
  5.         s1.append(s2);
  6.         s2.append("abcdef");
  7.         cout << s1 << endl;
  8.         cout << s2 << endl;
  9. }
复制代码
结果:

   这里传参可以是一个string类,也可以是常量字符串,都是直接在背面插入。
  string& append(const string&str,size_t subpos,size_t sublen) 
  1. void test8()
  2. {
  3.         string s1("abcdef");
  4.         string s2("hijklm");
  5.         s1.append(s2, 2, 3);
  6.         cout << s1 << endl;
  7. }
复制代码
结果:

   这里第一个参数是要插入的string类,第二个参数是从s2下标第几个位置开始插入,第三个参数是要插入的个数。
  operator+=:


 
  1. void test9()
  2. {
  3.         string s1("hello ");
  4.         string s2("world");
  5.         const char* s = "hhhhh";
  6.         s1 += s2;
  7.         cout << s1 << endl;
  8.         s2 += s;
  9.         cout << s2 << endl;
  10.         s1 += 'a';
  11.         cout << s1 << endl;
  12. }
复制代码
结果:

   这里和append非常相似,都是直接在背面追加字符,字符串,和string类,这种增加的方式用的较多,且代码可读性较高。
   c_str:

  1. void test9()
  2. {
  3.         string s1("hello ");
  4.         cout << s1.c_str() << endl;
  5. }
复制代码
结果:

   这里就是返回类内里的字符串,这里由于是c语言格式,所以字符末了有“\0”。
   find+nops:


这里讲常用的函数接口:
size_t find(char c, size_t pos=0) const:
  1. void test10()
  2. {
  3.         string s("hello world");
  4.         size_t pos = s.find(' ',0);
  5.         cout << pos << endl;
  6. }
复制代码
结果:

   这里是从pos位置,寻找第一次出现字符c的位置,并返回该位置的下标。
   rfind:


这里讲常用的函数接口:
size_t rfind(char c, size_t pos=0) const:
  1. void test10()
  2. {
  3.         string s("hello world hello");
  4.         size_t pos = s.rfind(' ');
  5.         cout << pos << endl;
  6. }
复制代码
结果:

   这里是从pos位置向前寻找,默认pos参数是npos,相称于是从末了开始向前找字符c,并返回该位置下标。
   substr:

  1. void test10()
  2. {
  3.         string s1("hello ");
  4.         string s2=s1.substr(1, 3);
  5.         cout << s2 << endl;
  6. }
复制代码
结果:

   这里函数是将s1从pos=1,位置开始,拷贝3个字符到s2上。
    总结: 1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差 不多,一样平常环境下string类的+=操纵用的比力多,+=操纵不仅可以连接单个字符,还可 以连接字符串。
  2. 对string操纵时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
   1.5 string类非成员函数

函数功能说明operator+尽量少用,由于传值返回,导致深拷贝服从低operator>>输入运算符重载operator<<输出运算符重载getline获取一行字符串relational_operator巨细比力   这里operator+,就是和operator+=差不多,都在背面增加字符,不建议用是由于这是传值返回,深拷贝服从太低了。
  operator>>和operator<<:

   这里和小编之前实现的Date日期类的用法一样,可以去之前的文章“赋值运算符重载”翻阅。 
   getline:


  1. void test11()
  2. {
  3.         string s;
  4.         getline(cin, s);
  5.         cout << s << endl;
  6. }
复制代码
结果:

   这里getline函数是流提取,但这里是读取一行的字符直到换行符,不会像cin或scanf到空格就制止了。
  1. void test11()
  2. {
  3.         string s;
  4.         getline(cin, s,'&');
  5.         cout << s << endl;
  6. }
复制代码
结果:

   这里可以再传一个参数,表示当遇到字符c才制止,乃至换行都不行。
   
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表