【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程序节,快要来了,大家会怎么样过呢


目录
打赏
0
0
0
0
5
分享
相关文章
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
2天前
|
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、&quot;+&quot;操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
课时44:String类对象两种实例化方式比较
本次课程的主要讨论了两种处理模式在Java程序中的应用,直接赋值和构造方法实例化。此外,还讨论了字符串池的概念,指出在Java程序的底层,DOM提供了专门的字符串池,用于存储和查找字符串。 1.直接赋值的对象化模式 2.字符串池的概念 3.构造方法实例化
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
46 1
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
|
2月前
|
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
74 19
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等