【C++】STL —— String类不会怎么办? 看文档(万字详解)(下)

简介: 【C++】STL —— String类不会怎么办? 看文档(万字详解)(下)

七、Modifiers 修改


🎨追加


0a2653c851af460fa595bd959398a8f1.png


+=最好用也最常用,因为既可以追加字符、也可追加字符串 ,其实底层调用了append和push_back


void test_string7()
{
  string s("hello");
  s.push_back('-');
  s.push_back('-');
  s.append("world");
  cout << s << endl;
  string str("我来了");
  s += '@';//字符
  s += str;//字符串
  s += "JDG总冠军";
  cout << s << endl;
}

0a2653c851af460fa595bd959398a8f1.png

其中append的迭代器可以选择性的打印字符串内容(了解即可)


string s("hello");
string str("我来了");
s.append(++str.begin(), --str.end());
string copy(s.begin() + 5, s.end() -5);

2d65d23f6d4748949b924e4057485923.png

下面来研究尾插扩容容量变化 ——


void test_string8()
{
  string s;
  size_t sz = s.capacity();
  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';
  }
  }
}


我们可以看见在vs下增容,第一次是两倍,后面是1.5倍的增容


0a2653c851af460fa595bd959398a8f1.png


如果提前知道是多少空间,可以调用reserve预留空间,避免频繁增容的消耗


s.resize(1000 , 'x'); //开空间+初始化


2d65d23f6d4748949b924e4057485923.png


🎨插入和删除


🐋尽量少使用头部的插入和删除,因为要挪动,O(N)效率低


0a2653c851af460fa595bd959398a8f1.png


🤞1️⃣小练习:在字符串中空格的地方插入一个%


我的第一想法:遍历字符串,遇到' '的时候,直接插入


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
  }
  }
  cout << str << endl;
}


但这样有没有问题呢?


0a2653c851af460fa595bd959398a8f1.png


那怎么样处理比较好呢?只需要在插入的地方i额外的+3,


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
    i += 3;
  }
  }
  cout << str << endl;
}


2️⃣练习升级:把字符串中遇到的空格替换成20%


这就要引入erase:删除字符串中的字符


2d65d23f6d4748949b924e4057485923.png


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
    i += 3;
  }
  }
  //再把空格删除掉哦
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.erase(i, 1);
  }
  }
  cout << str << endl;
}


ps:最好不要自行挪动数据,因为[]会检查下标位置必须要小于size的,如果真的要挪动,要resize一下,增加长度


也有一种空间换时间的方法:创建新的串遍历,效率O(N)


void test_string9()
{
  string str("JDG NB 总冠军");
  string newstr;
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] != ' ')
  {
    newstr += str[i];
  }
  else
  {
    newstr += "20%";
  }
  }
  cout << newstr << endl;
}


八、String operations 字符串操作


打印字符串,都能打印,但意义不同 ——


0a2653c851af460fa595bd959398a8f1.png


🤞前者是string类的流插入运算符的重载,以对象size为准,size是多少打印多少

后者是以常量字符串对象\0为准,遇到\0就结束(符合c语言标准)


所以说\0不一定是结束标志,在string里会被忽视


主要作用还是与函数接口接合——


string file("test.txt");  
  FILE* fout = fopen(s.c_str(), "w");//打印文件


![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/631a160b51da4d7e9729bbeb05c5295d.png


🌍substr 子串

➰· 取当前串的一个子串


4cebaac233b3433da32a72337a77fc60.png


len:如果len比能取到的串长或使用缺省值npos,都是能取多少取多少


🌍查找 find & rfind

🌊 1. 从字符串pos位置从前向后找字符c/字符串,返回该字符在字符串中的位置,找不到就返回npos


6de278e6d6694ce5bb08e7e842b7e74b.png


🌊 2. 从字符串pos位置倒着找找字符c/字符串,返回该字符在字符串中的位置


8ec4f2997fb246878c34ecd6d122b7c6.png


💢小练习:取字符串的后缀


void test_string11()
{
  string str("test.cpp");
  //找出后缀
  size_t pos = str.rfind('.');//面对多后缀的最好用 rfind
  if (pos != string::npos)
  {
  //string buff = str.substr(pos, str.size() - pos);
  string buff = str.substr(pos);//因为是取到结束
  cout << buff << endl;
  }
}


但是这样写有没有说明问题呢?如果后缀是test.cpp.tar.zip呢?要取最后一个后缀


这样就要将find ——》 rfind,即可解决


💢解析出网址的这三个部分:协议 - 域名 - 资源


#include<iostream>
#include<string>
using namespace std;
int main()
{
  string url = "https://cplusplus.com/reference/string/string/substr/";
  size_t pos1 = url.find("://");
  if (pos1 == string::npos)
  {
  cout << "非法字符串" << endl;
  return;
  }
  //取协议
  string protocol = url.substr(0, pos1);
  cout << protocol << endl;
  size_t pos2 = url.find('/', pos1 + 3);
  if (pos1 == string::npos)
  {
  cout << "非法字符串" << endl;
  return;
  }
  string domain = url.substr(pos1 + 3, pos2 - pos1 - 3);//取域名
  cout << domain << endl;
  string uri = url.substr(pos2 + 1);//取资源
  cout << uri << endl;
}


💢find 和find_first_of 区别

0a2653c851af460fa595bd959398a8f1.png

find_first_of:只要出现要寻找的串里的任意字符都找出来


吐槽一下:find_first_of 更应该叫find_any_of,但我们要尊重语法


2d65d23f6d4748949b924e4057485923.png


九、非成员函数重载


💦流插入&流提取

注意注意:流插入和流提取都是以空格、回车作为结束标志的。这意味着如果想要输入一个字符串,最终可能只读入了一个单词,剩余的留在缓冲区里。


0a2653c851af460fa595bd959398a8f1.png


于是我们引入了getline,做题会遇到


💦getline


2d65d23f6d4748949b924e4057485923.png


遇到换行才结束


💢小练习:输出一个整数,表示输入字符串最后一个单词的长度


#include <iostream>
#include <string>
using namespace std;
int main() {
    string str;
    getline(cin,str);
    size_t pos = str.rfind(' ');
    if(pos != string::npos)
    {
        cout<<str.size()-pos-1<<endl;
    }
    else{
        cout<<str.size()<<endl;
    }
}


💦to_string


0a2653c851af460fa595bd959398a8f1.png

void test_string12()
{
  //整形转字符串
  int ival;
  double dval;
  cin >> ival >> dval;
  string istr = to_string(ival);
  string dstr = to_string(dval);
  cout << istr << endl;
  cout << dstr << endl;
  //字符串转整形
  istr = "9999";
  dstr = "9999.99";
  ival = stoi(istr);
  dval = stod(dstr);
}


重要的事情说三遍:不会的查文档,不会的查文档,不会的查文档!!

2d65d23f6d4748949b924e4057485923.png


十、 vs和g++下string结构的说明(了解)


🎉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个字节。


2d65d23f6d4748949b924e4057485923.png


🎉g++下string的结构

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:


空间总大小

字符串有效长度

引用计数

指向堆空间的指针,用来存储字符串。

struct _Rep_base
{
  size_type _M_length;
  size_type _M_capacity;
  _Atomic_word _M_refcount;
};


📢写在最后

1024程序节,快要来了,大家会怎么样过呢


相关文章
|
2天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
2天前
|
C++
模拟实现c++中的string
模拟实现c++中的string
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19
|
1月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
50 13
|
1月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
52 5
|
1月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
40 5
|
1月前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
48 4
|
1月前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
32 3
|
3月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
89 2
|
3月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
156 5