当前位置: 首页 > news >正文

【C++笔记】string类使用详解

前言

各位读者朋友们大家好!上期我们讲完了C++的模板初阶,这一期我们开启STL的学习。STL是C++的数据结构和算法库,是我们学习C++的很重要的一部分内容,在以后的工作中也很重要。现在我们开始讲解。

目录

  • 前言
  • 一. 为什么学习string类
    • 1. C语言中的字符串
  • 二. 标准库中的string类
    • 2.1 string类
    • 2.2 auto和范围for
    • 2.3 string类的常用接口说明
      • 1. string类对象的常见构造
      • 2. string类对象的容量操作
        • 2.1 size和lenth
        • 2.2 capacity
        • 2.3 empty
        • 2.4 clear
        • 2.5 reserve
        • 2.6 resize
      • 3. string类的访问及遍历操作
        • 1. 迭代器(iterator)
        • 2.operator[]访问
        • 3. [] + 下标遍历
        • 4. begin + end遍历
        • 5. rbegin + rend遍历
        • 6. 范围for遍历
      • 4. string类对象的修改操作
        • 4.1 push back
        • 4.2 append
        • 4.3 operator+=
        • 4.4 insert
        • 4.5 erase
      • 5. string类的其他相关操作
        • 5.1 c_str
        • 5.2 find + npos
        • 5.3 substr
        • 5.4 rfind
        • 5.5 find_first_of 和 find_last_of
        • 5.6 find_first_not_of 和 find_last_not_of
        • 6.getline
  • 结语

一. 为什么学习string类

1. C语言中的字符串

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但这些库函数与字符串是分开的,不太符合OOP(面向对象编程,Object-Oriented Programming是一种广泛使用的编程范式,它基于“对象”的概念来设计和实现软件。)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

二. 标准库中的string类

2.1 string类

string类的文档
在使用string类时,必须包含#include< string >头文件以及指明命名空间std;

2.2 auto和范围for

auto关键字
在这里补充两个C++11的语法,方便后续学习:

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推到而得。
    在这里插入图片描述

  • 用auto声明指针类型时,用auto和auto * 没有区别,但是auto声明引用类型的时候必须加&

  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
    在这里插入图片描述

  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
    在这里插入图片描述

  • auto不能用来直接声明数组
    在这里插入图片描述
    范围for

  • 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会犯错,因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“:”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判结束。

  • 范围for可以作用到数组和容器对象上进行遍历
    在这里插入图片描述

  • 范围for的底层很简单,容器遍历实际就是替换迭代器,这个在汇编层面可以看到


2.3 string类的常用接口说明

1. string类对象的常见构造

在这里插入图片描述

  • 1.string()
    在这里插入图片描述

这是string的无参构造,构造一个长度为0的空字符串。

int main()
{string s1;cout << s1;return 0;
}

在这里插入图片描述


  • 2. string (const string& str);
    在这里插入图片描述
    这是string类的拷贝构造
int main()
{string s1;string s2(s1);cout << s2;return 0;
}

在这里插入图片描述


  • 3. string (const string& str, size_t pos, size_t len = npos);
    在这里插入图片描述
    从字符串的指定位置(我们给的是下标)拷贝len个字符,如果字符串太短或着len是npos就拷贝到字符串结束。
int main()
{string s("hello world");string s1(s, 0, 5);string s2(s, 0, 15);string s3(s, 0);// 第三个参数使用缺省值nposcout << s1 << endl;cout << s2 << endl;cout << s3 << endl;return 0;
}

在这里插入图片描述


  • 4.string (const char * s);
    在这里插入图片描述
    这是string类的带参构造
int main()
{string s("hello world");cout << s << endl;return 0;
}

在这里插入图片描述


  • 5. string (const char * s, size_t n);
    在这里插入图片描述
    从s指向的字符数组中,复制前n个字符
int main()
{string s("hello world", 5);cout << s;return 0;
}

在这里插入图片描述


  • 6. string (size_t n, char c);
    在这里插入图片描述
    用n个字符c来构造字符串
int main()
{string s(5, 'h');cout << s;return 0;
}

在这里插入图片描述


2. string类对象的容量操作

函数名称功能说明
size(重点)返回字符串的有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty(重点)检测字符串是否为空串,是返回True,否则返回False
clear(重点)清空有效字符
reserve(重点)为字符串预留空间
resize(重点)将有效字符的个数改成n个,多出的空间用字符c填充
2.1 size和lenth

size和lenth都是用来求字符串长度的
在这里插入图片描述

2.2 capacity

capacity是返回字符串的容量的,跟顺序表中的capacity一样,但是string中的capacity不包含\0
在这里插入图片描述
在实现顺序表的时候,我们对顺序表的扩容是二倍扩容,在Vs环境中,编译器对string类的扩容是怎样扩的呢?我们通过下面的一段代码看一下:

void TestPushBack()
{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中,string类的实现加了一个buff数组,当字符长度小于16时,就存在buff数组中,如果大于16就去堆上开空间,看一下string的大小:
在这里插入图片描述
在32位环境下,按照上面string类的底层内存对齐计算得string类的大小也是28字节,不管使用数组还是在堆上开空间。
在这里插入图片描述
在Linux环境中使用g++编译器,是完全的2倍扩容
在这里插入图片描述

2.3 empty
2.4 clear

在这里插入图片描述
将字符串清空,空间清不清空看编译器,Vs和g++都不清

int main()
{string s("hello world");cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;s.clear();cout << s.size() << endl;cout << s.capacity() << endl;return 0;
}

在这里插入图片描述

2.5 reserve

在这里插入图片描述
reserve用来预留空间,预留的空间在Vs环境下会大于等于我们给定的空间,这与Vs的内存对齐有关,在g++环境下会严格等于要求的大小。
在这里插入图片描述
那如果预留的空间过大,编译器会不会缩容呢?
在这里插入图片描述
C++标准并没有强制要求缩容,看编译器,但是有一点是确定的:这个函数不会改变字符串的长度和内容。

int main()
{string s("********************");cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(15);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(28);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(48);cout << s.size() << endl;cout << s.capacity() << endl << endl;return 0;
}

在这里插入图片描述
上面可以看到,在Vs环境下,是不会缩的,但是在g++环境下编译器是会进行缩容的。

2.6 resize

在这里插入图片描述
在这里插入图片描述

int main()
{string s("************************");//24cout << s.size() << endl;cout << s.capacity() << endl;s.resize(18);cout << s.size() << endl;cout << s.capacity() << endl;s.resize(28, 'x');cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;s.resize(36, '#');cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;return 0;
}

在这里插入图片描述

3. string类的访问及遍历操作

函数名称功能说明
operator[](重点)返回pos位置的字符,const string类对象调用
begin+endbegin获取开始位置字符的迭代器+end获取最后一个字符下一个位置的迭代器
rbegin+rendrbegin获取最后一个字符的迭代器+rend获取第一个字符前一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式
1. 迭代器(iterator)

在C++中,迭代器(Iterator)是一种用于访问容器(如数组、向量、列表、集合等)中元素的对象。迭代器提供了一种统一的方式来遍历容器中的元素,以下是迭代器的主要作用:

1.遍历容器
2.访问元素
3. 范围操作
4. 支持泛型编程:迭代器使得算法和容器可以分离,从而实现高层次的抽象和泛型编程。通过定义迭代器类型,容器可以与各种算法无缝集成。
5. 插入和删除操作
6. 统一接口:迭代器提供了一个统一的接口来访问和操作容器中的元素,使得用户可以用相同的方式处理不同类型的容器。

迭代器共有四种:
在这里插入图片描述

2.operator[]访问

string类重载了[]这一操作符,返回的是字符串的任意位置的字符的引用,这样我们就可以修改字符串某一位置的值了,并且越界访问还会报错。

int main()
{string s("hello world");cout << s[4] << endl;//cout << s[20];s[4] = 'x';cout << s << endl;return 0;
}

在这里插入图片描述
我们可以认为string类是如下结构:

class string
{
public:char& operator[](size_t i){assert(i < _size);return _str[i];}
private:char* _str;size_t _size;size_t _capacity;
};

operator[]封装在类里面作为成员函数默认为内联,效率也会很高。

3. [] + 下标遍历
int main()
{string s("hello world");for (int i = 0; i < s.size(); ++i){cout << s[i] << " ";}return 0;
}

在这里插入图片描述
这种方式像数组访问一样

4. begin + end遍历
int main()
{string s("hello world");string::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}return 0;
}

在这里插入图片描述
在这里插入图片描述
这种遍历方式所有的容器都可以使用这种方式
当字符串被const修饰时,使用string::const_interator迭代器遍历:
在这里插入图片描述

5. rbegin + rend遍历

rebegin和rend是反向迭代器(string::reverse_iterator)的函数,rbegin获取最后一个字符的迭代器+rend获取第一个字符前一个位置的迭代器。这样方式的遍历是反向遍历

int main()
{string s("hello world");string::reverse_iterator rit = s.rbegin();while (rit != s.rend()){cout << *rit << " ";++rit;}return 0;
}

在这里插入图片描述

6. 范围for遍历
int main()
{string s("hello world");for (auto ch : s){cout << ch << " ";}return 0;
}

在这里插入图片描述

范围for是自动推导出类型,自动遍历的,也就是说编译器推导出s的类型,然后将其拷贝赋值给ch。
在这里插入图片描述
在汇编层面看,范围for的底层就是迭代器。
在这里插入图片描述
我们发现,对ch进行修改并没有影响s,这是因为ch是字符串的拷贝,而对迭代器进行操作就会影响到s,我们可以将迭代器理解为指针,对指针解引用修改,想要通过范围for修改s的内容,可以给auto后面加引用,这样ch就是s里面每个变量的别名。
在这里插入图片描述

4. string类对象的修改操作

函数名称功能说明
push back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=(重点)在字符串后面追加字符串
insert在pos位置插入字符
erase在pos位置删除len长度的字符
4.1 push back

在这里插入图片描述
在字符串的后面尾插一个字符

int main()
{string s("hello wodld");s.push_back('x');cout << s << endl;return 0;
}

在这里插入图片描述

4.2 append

在字符串的末尾追加一个新的字符串

int main()
{string s("hello world");s.append(" hello Yuey");cout << s << endl;return 0;
}

在这里插入图片描述

4.3 operator+=

在这里插入图片描述
重载了+=操作符,可以在字符串的末尾追加string类、字符串以及字符

int main()
{string s1("hello world");string s2("hello world");string s3("hello world");// 追加string类string s4(" hello Yuey");s1 += s4;cout << s1 << endl;// 追加字符串s2 += " hello Yuey";cout << s2 << endl;// 追加字符s3 += "*";cout << s3 << endl;
}

在这里插入图片描述
这个+=在实践中使用最多

4.4 insert

在pos位置插入字符或字符串,也可以使用迭代器
在这里插入图片描述

int main()
{string s("hello world");string s1(" hello Yuey");//s.insert(5, "#");// 插入一个字符s.insert(5,1,'#');s.insert(12, s1);cout << s << endl;// 迭代器s.insert(s.begin(), '*');cout << s << endl;return 0;
}

在这里插入图片描述

4.5 erase

在这里插入图片描述
erase支持在指定位置删除len长度的字符,如果len不传的话默认删除npos也就是删除pos位置及以后的所有字符,erase也支持迭代器删除,但是用的最多的还是第一种

int main()
{string s1("hello world");// 头删s1.erase(0, 1);cout << s1 << endl;s1.erase(s1.begin());cout << s1 << endl;// 尾删s1.erase(--s1.end());cout << s1 << endl;s1.erase(s1.size() - 1, 1);cout << s1 << endl;// 指定位置删除string s2("hello Yuey");s2.erase(6, 4);cout << s2 << endl;// 指定位置不传长度s2.erase(1);cout << s2 << endl;return 0;
}

在这里插入图片描述

5. string类的其他相关操作

函数名功能说明
c_str(重点)返回c格式字符
find + npos(重点)从字符串pos位置开始往后找到字符c,返回该字符在字符串中的位置
rfind从字符串的pos位置开始往前找字符c,返回该字符在字符串中的位置
substr从str中从pos位置开始,截取n个字符,然后将其返回
5.1 c_str

在这里插入图片描述
这个函数的目的主要是兼容C语言,返回的是指向数组的指针,主要在文件读取时使用

5.2 find + npos

在这里插入图片描述
find支持在pos位置开始正向查找string类、字符串以及字符,返回值是第一次找到的指定字符的下标,没找到返回npos,如果pos不传,默认从第一个字符开始
将字符串中的空格替换为%:

int main()
{string s("hello world hello Yuey");cout << s << endl;size_t pos = s.find(' ', 0);while (pos != string::npos){s.replace(pos, 1, "%");pos = s.find(' ', pos + 1);}cout << s << endl;return 0;
}

在这里插入图片描述
这里用到了replace函数:
在这里插入图片描述
replace函数就是将pos及以后位置的len长度的字符替换为给定的字符

int main()
{string s1("hello world");s1.replace(0, 2, "***");cout << s1 << endl;string s2("hello Yuey");s2.replace(5, 3, "***");cout << s2;return 0;
}

在这里插入图片描述
但是上面的代码在空格极多的时候效率极低,我们可以用空间换时间的方法:

int main()
{string s("hello world hello Yuey");cout << s << endl;string tmp;for (auto ch : s){if (ch == ' '){tmp += "%";}else{tmp += ch;}}s = tmp;cout << s << endl;return 0;
}
5.3 substr

在这里插入图片描述
substr是返回从pos位置开始的后面len长度的子字符串,如果没传len函数使用缺省值npos,返回pos位置及以后的所有字符。

int main()
{string s("hello world");string S1;S1 = s.substr(5);cout << S1;return 0;
}

在这里插入图片描述

5.4 rfind

在这里插入图片描述
rfind是在pos位置反向查找string类、字符串以及字符,并返回第一次出现该字符的下标,没找到返回npos

int main()
{string s("Test.cpp.zip");// 看文件类型size_t pos = s.rfind('.');string s1 = s.substr(pos);cout << s1;return 0;
}
5.5 find_first_of 和 find_last_of

find_first_of:
在这里插入图片描述
这个函数是在字符串中找给定的字符或字符串中的所有字符,并返回第一次找到的地址

int main()
{string s("abcdefggfedcba");size_t pos = s.find_first_of("abc");while (pos != string::npos){s[pos] = '*';pos = s.find_first_of("abc", pos + 1);}cout << s;return 0;
}

在这里插入图片描述
find_last_of:
在这里插入图片描述
这个可以理解为反向找

void SplitFilename(const std::string& str)
{std::cout << "Splitting: " << str << '\n';std::size_t found = str.find_last_of("/\\");std::cout << " path: " << str.substr(0, found) << '\n';std::cout << " file: " << str.substr(found + 1) << '\n';
}int main()
{std::string str1("/usr/bin/man");std::string str2("c:\\windows\\winhelp.exe");SplitFilename(str1);SplitFilename(str2);return 0;
}

这样就能实现文件的路径和文件名分离

5.6 find_first_not_of 和 find_last_not_of

这两个函数是从字符串中找除了给定字符串里的的字符,并返回第一次找到的下标

int main()
{string s("abcdefggfedcba");size_t pos = s.find_first_not_of("abc");while (pos != string::npos){s[pos] = '*';pos = s.find_first_not_of("abc", pos + 1);}cout << s;return 0;
}

在这里插入图片描述

6.getline

在讲getline之前我们先看一道题目: 字符串最后一个单词的长度
在这里插入图片描述
这种情况下就需要借助getline函数了:getline
在这里插入图片描述
getline默认读取到换行的时候读取结束,读取结束的条件也可以自己设定
在这里插入图片描述
在这里插入图片描述自定义结束条件

结语

以上就讲完了string类的基本用法,对于string类的模拟实现我们下期再讲,感谢大家的阅读,欢迎大家批评指正!

http://www.lryc.cn/news/474978.html

相关文章:

  • 数字隔离器与光隔离器有何不同?---腾恩科技
  • 方差与协方差
  • 【含文档】基于Springboot+Vue的工商局商家管理系统 (含源码数据库+LW)
  • 【股票市场情绪量化模型】
  • Oracle视频基础1.3.8与1.4.1练习
  • 基于前馈神经网络模型和卷积神经网络的MINIST数据集训练
  • Vue3中Element Plus==el-eialog弹框中的input无法获取表单焦点
  • 16.网工入门篇--------介绍下网络服务及应用
  • 区分 electron 全屏和最大化
  • 封装一个请求的hook(react函数组件)
  • c语言内存块讲解
  • 2024年10月23日Github流行趋势
  • YOLOv6-4.0部分代码阅读笔记-dbb_transforms.py
  • C++ 基础语法 一
  • B2020 分糖果
  • VBA字典与数组第二十讲:如何在代码运行时创建数组
  • 字符串统计(Python)
  • NVR小程序接入平台/设备EasyNVR多个NVR同时管理视频监控新选择
  • 怎样能把图片做压缩处理?学会4款在线工具高效压缩图片
  • ZooKeeper 客户端API操作
  • 常用滤波算法(一)-限幅滤波法
  • 江协科技STM32学习- P33 实验-软件I2C读写MPU6050
  • BusHound工具的使用-调试USB
  • Hadoop生态圈框架部署(四)- Hadoop完全分布式部署
  • Spring Boot 与 Vue 共铸卓越采购管理新平台
  • leetcode3. Longest Substring Without Repeating Characters
  • Mongodb使用视图连接两个集合
  • SIP是什么?
  • Day 39 || 01背包、416. 分割等和子集
  • 调用detr-resnet-50进行目标检测