【C++从练气到飞升】09---string语法指南(二)

简介: 【C++从练气到飞升】09---string语法指南(二)

【C++从练气到飞升】09---string语法指南(一)+https://developer.aliyun.com/article/1502607

如上当不写s.reserve(100);就会发生扩容,但是当写上s.reserve(100);提前开好空间就不会发生扩容,同时要注意s.reserve(10);并不会缩减空间(缩容)

  • resize (重点)- - - 将有效字符的个数改成n个,多出的空间用字符c填充
void  test_string2()
{
  string s1("hello world");
  cout << s1 << endl;
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  //s1.resize(13, 'x');
  
  s1.resize(20, 'x');//这里有效字符个数改成20s1本来的有效字符hello world是11个超出部分用x补充,其次size()和capacity也会随之发生改变--->size()变成20;capacity()变成31
  
  s1.resize(5);
  cout << s1 << endl;
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  string s2;
  s2.resize(10, '#');
  cout << s2 << endl;
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
}

  • shrink_to_fit() - - - 将capacity容量缩至合适
void test_string1()
{
  string s;
  s.reserve(50);
  s += "hello world";
  cout << s.size() << endl;
  cout << s.capacity() << endl;
  s.shrink_to_fit();
  cout << s.size() << endl;
  cout << s.capacity() << endl;
}

注意:

  • size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
  • clear()只是将string中有效字符清空不改变底层空间大小
  • resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  • reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时reserver不会改变容量大小
2.3 string类对象的访问及遍历操作

  • operator[] (重点) 返回pos位置的字符,const string类对象调用
void test_string2()
{
  string s1("hello world\n");
  string s2 = "hello world";//单参数构造支持隐式类型转换
  //遍历string
  for (size_t i = 0; i < s1.size(); i++)
  {
    //读
    cout << s1[i] << " ";
  }
  cout << endl;
  for (size_t i = 0; i < s1.size(); i++)
  {
    //写
    s1[i]++;
  }
  cout << s1 << endl;;
}
  • begin+ end - - - begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
void test_string3()
{
  string s4="hello world";
  string::iterator it = s4.begin();
  while (it != s4.end())
  {
    //读
    cout << *it << " ";
    it++;
  }
  cout << endl;
  it = s4.begin();
  //while (it < s4.end())可以这样写但是不建议
  while (it != s4.end())
  {
    //写
    *it='a';
    cout << *it << " ";
    it++;
  }
  cout << endl;
}
  • rbegin + rend - - - begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
void test_string4()
{
  string s5 = "hello world";
  string::reverse_iterator rit = s5.rbegin();
  while (rit != s5.rend())
  {
    cout << *rit << " ";
    ++rit;
  }
  cout << endl;
}
  • 范围for - - - C++11支持更简洁的范围for的新遍历方式
void  test_string5()
{
  string s6 = "hello world";
  //原理:编译器替换成迭代器
  for (auto ch : s6)
  {
    //读
    cout << ch << " ";
  }
  cout << endl;
  //对于写---范围for 本质自动遍历是*it赋值给ch,ch是*it的拷贝所以要写的话要加&,这样ch就是*it的别名
  for (auto ch : s6)//错误写法
  for (auto& ch : s6)
  {
    //写
    ch++;
  }
  cout << s6 << " ";
  cout << endl;
}
void func(const string s)//不推荐传值传参,会进行拷贝调用拷贝构造string的底层不能用浅拷贝所以用引用+const
void func(const string& s)
{
  //迭代器支持读写,但是这里是const不支持迭代器写所以C++设计出了cbegin() cend() crbegin() crend()也可以+const例如下面一行注释代码
  //string::const_iterator it = s.begin();
  //对比于上面代码+const和不+const还用修改可以直接使用auto直接推出类型
  auto it = s.begin();
  while (it != s.end())
  {
    //读
    cout << *it << " ";
    it++;
  }
  cout << endl;
  //string::const_reverse_iterator rit = s.rbegin();
  auto rit = s.rbegin();
  while (rit != s.rend())
  {
    cout << *rit << " ";
    ++rit;
  }
  cout << endl;
}
void test_string6()
{
  string s7 = "hello world";
  func(s7);
}
2.4 string类对象的修改操作

  • push_back - - - 在字符串后尾插字符c
void test_string3()
{
  string s;
  string ss("hello");
  s.push_back('#');
  cout <<s<< endl;
}

  • append - - - 在字符串后追加一个字符串
void test_string3()
{
  string s;
  string ss("hello");
  s.append("hello world");
  s.append(ss);
  cout <<s<< endl;
}

  • operator+= (重点) - - - 在字符串后追加字符串str
void test_string3()
{
  string s;
  string ss("hello");
  s += '#';
  s += "hello";
  s += ss;
  cout << s << endl;
}

  • c_str(重点) - - - 返回C格式字符串
void test_string9()
{
  string filename;
  cin >> filename;
  FILE* fout = fopen(filename.c_str(), "r");
}
  • find + npos(重点) - - - 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
void test_string2()
{
  string s1("test.cpp");//读取文件后缀
  size_t i = s1.find('.');
  string s3 = s1.substr(i);
  cout << s3 << endl;
}

  • rfind - - - 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
void test_string2()
{
  string s2("test.cpp.tar.zip");//找文件后缀.zip
  size_t j = s2.rfind('.');//倒着找
  string s4 = s2.substr(j);
  cout << s4 << endl;
}

  • substr - - - 在str中从pos位置开始,截取n个字符,然后将其返回
void test_string2()
{
  string s1("test.cpp");//读取文件后缀
  size_t i = s1.find('.');
  string s3 = s1.substr(i);
  cout << s3 << endl;
}

注意:

  1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
2.5 string类非成员函数

  • operator+ - - - 尽量少用,因为传值返回,导致深拷贝效率低
void test_string3()//at失败后会抛异常
{
  string ss("hello");
  string ret=ss + '#';//+是一个传值返回,代价比较大
  string ret2 = ss + "hello";
  cout << ret << endl;
  cout << ret2 << endl;
  cout << endl;
}

  • operator>> (重点)- - - 输入运算符重载
  • operator<< (重点) - - - 输出运算符重载
  • getline (重点) - - - 获取一行字符串

🌟cin>> 和getline的区别在于:>>遇到空格’ '和换行\n会截止,而getline默认只有遇到换行\n才截止,因此当我们需要从键盘读取一个含有空格的字符串是,只能用getline

void test_string3()
{
  string s1;
  getline(cin, s1, '!');
  cout << s1;
}

  • relational operators (重点)- - - 大小比较
2.6 string类类型转换接口
  • string类型转换成内置类型

内置类型转换成string

2.7 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个字节,内部只包含了一个指针,该指

针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};
  • 指向堆空间的指针,用来存储字符串。
相关文章
|
24天前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
50 5
|
24天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
38 2
|
2月前
|
C++ 容器
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
C++ 容器
|
2月前
|
C++ 容器
|
2月前
|
C语言 C++
深度剖析C++string(中)
深度剖析C++string(中)
57 0
|
2月前
|
存储 编译器 程序员
深度剖析C++string(上篇)(2)
深度剖析C++string(上篇)(2)
43 0
|
2月前
|
存储 Linux C语言
深度剖析C++string(上篇)(1)
深度剖析C++string(上篇)(1)
33 0
|
2月前
|
C++