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

【C++】深入理解string类(1)

目录

一  认识了解string类

 1 为什么学习string类?

2. 标准库中的string类 

二  string类的常用接口说明

1. string类对象的常见构造

2 赋值

3 遍历和修改

4 数据个数

5 迭代器

6 迭代器的分类

(1)begin

(2)rbegin

(3)cbegin

7 查找算法

三 auto和范围for

auto关键字

范围for

四 完整代码


 在使用string类的时候,要加上头文件#include<string>


一  认识了解string类

 1 为什么学习string类?

1.1string类的定义

在 C++ 中,string 类是标准库(STL 的一部分)提供的用于处理字符串的模板类,定义在 <string> 头文件中,属于 std 命名空间。它封装了字符串的存储和操作,相比 C 语言中的字符数组(char*),string 类提供了更安全、便捷的字符串处理方式。

  1.2 C语言中的字符串 
  C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
  的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
  自己管理,稍不留神可能还会越界访问

1.3 两个面试题(暂不做讲解) 

https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e?tpId=13&&tqId=11202&rp=6&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

https://leetcode-cn.com/problems/add-strings

在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、 快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

2. 标准库中的string类 


2.1 string类

string类的文档介绍:

   http://www.cplusplus.com/reference/string/string/?kw=string

当我们在看这个文档的时候发现,里面几乎是全英文的界面,我们要自己学会看文档,当然也可以使用翻译软件,但是翻译出来的意思总是和原本要表达的意思差点“味道”,所以更推荐自己阅读英语的文档(上半年C++网站的官方中文版下线了)

在使用string类时,必须包含#include头文件以及using namespace std;

二  string类的常用接口说明


string类的接口有一百多个,我们不可能每一个都学习,在这里我们学习最常用的二十几个接口。

1. string类对象的常见构造

(constructor)函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const  char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点)拷贝构造函数
void Teststring()
{string s1;                // 构造空的string类对象s1string s2("hello bit");   // 用C格式字符串构造string类对象s2string s3(s2);            // 拷贝构造s3
}

我们来学习一下第三个:

使用方法翻译过来是这样的:复制从pos位置开始并跨越len字符的str部分,如果str太短或len是string::npos,则直到str的末尾。

我们来使用一下:

void test_string1()
{string s4(s2, 0, 5);cout << s4 << endl;// pos位置拷贝结尾string s5(s2, 6, 15);cout << s5 << endl;string s6(s2, 6);cout << s6 << endl;}

如果要复制的长度超过了字符串的长度,编译器不会报错也不会越界,只会拷贝到字符串的末尾。

如果确定了要拷贝到末尾,可以直接省去第三个参数,如上面所示的s6。

但是这个接口我们并不常用,只是通过这个接口,我们了解到如何去阅读文档。

再引入一下第五个:

void test_string1()
{//只取前六个字符string s7("hello world", 6);cout << s7 << endl;//给s8赋值为十个*string s8(10, '*');cout << s8 << endl;s7 = "xxxx";cout << s7 << endl;
}

运行结果为:

Hello
**********

2 赋值

可以用字符赋值,也可以用字符串赋值

3 遍历和修改

pos是下标,返回的是pos对应位置的引用,意味着既可以读也可以修改。

void test_string2()
{string s1("hello world");cout << s1 << endl;s1[0] = 'x';cout << s1 << endl;cout << s1[0] << endl;// 越界有严格断言检查//s1[12];  // 断言//s1.at(12); // 抛异常cout << s1.size() << endl; // 推荐cout << s1.length() << endl;// 下标+[]// 遍历 or 修改for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1 << endl;
}

我们发现,[]的使用和数组下标的使用很相似

同样的:

at的使用和[]的使用类似,区别在于越界时at会抛异常

4 数据个数

这两个相比,我们更推荐使用size,有些时候例如链表,无法使用length,但是size在任何情况都适用。

cout << s1.size() << endl; // 推荐cout << s1.length() << endl;

5 迭代器

迭代也就是遍历的意思,迭代器是一种通用的访问所有容器的方式

迭代器的常见类型:iterator

在 C++ 中,iterator(迭代器)是一种行为类似指针的对象,用于遍历容器(如 vectorliststring 等)中的元素。它是连接容器和算法的桥梁,允许我们在不暴露容器内部实现细节的情况下访问元素。

iterator 的核心作用:

  • 提供统一的接口遍历不同容器(无论容器底层是数组、链表还是树结构)
  • 支持通过 * 运算符访问元素(类似指针解引用)
  • 支持通过 ++ 运算符移动到下一个元素
  • 支持比较操作(如 ==!= 判断是否到达容器末尾)

注意:行为像指针,但是底层不一定是指针

使用实例:

// 行为像指针一样的东西string::iterator it1 = s1.begin();while (it1 != s1.end()){// (*it1)--; 修改值的内容cout << *it1 << " ";//解引用输出对应的值++it1;向下一位挪动}cout << endl;

使用范围:左闭右开[begin , end)

这一点和python中的range的使用范围很像

这个时候有些同学就有疑问了,那这个迭代器和下标+[ ]不是一样的吗?为什么还要多此一举用迭代器。

// 下标+[]// 遍历 or 修改for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1 << endl;// 行为像指针一样的东西string::iterator it1 = s1.begin();while (it1 != s1.end()){// (*it1)--; 修改cout << *it1 << " ";++it1;}cout << endl;

是的,在这个应用场景我们使用下标会更方便简洁一点,但是 迭代器作为STL的一大组件,它是作为通用的访问所有容器的方式。底层是数据的结构(如顺序表)用下标+[ ],但是链表没有[ ],就需要借助迭代器。

我们来实现一下链表的迭代器:

list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);list<int>::iterator lit = lt.begin();while (lit != lt.end()){cout << *lit << " ";++lit;}cout << endl;

代码解析:

  1. list<int> lt;定义了一个存储 int 类型的双向链表。

  2. lt.push_back(1); 等向链表尾部依次添加 1、2、3。

  3. list<int>::iterator lit = lt.begin();定义一个迭代器,指向链表的第一个元素。

  4. while (lit != lt.end())从当前位置开始遍历,直到到达链表末尾。

  5. cout << *lit << " "; ++lit;输出当前迭代器指向的值,然后移动到下一个元素。

迭代器的特点

1、提供统一的方式遍历容器;

2、算法可以泛型化,算法借助迭代器处理容器的数据。

6 迭代器的分类

(1)begin

分为普通的迭代器和const迭代器,普通的的迭代器我们前面已经讲过了,下面来学习const迭代器。

//const string::iterator it1 = s.begin();string::const_iterator it1 = s.begin();while (it1 != s.end()){// *it1 = 'x'; // 不能修改cout << *it1 << " ";++it1;}cout << endl;

注意const的位置:在类名之后。这样修饰的是迭代器所指向的对象不被修改,如果放在类名之前,就是迭代器不能修改,这样是错误的。

(2)rbegin

反向迭代器和const反向迭代器

它的遍历是从后往前遍历的

string::const_reverse_iterator it2 = s.rbegin();while (it2 != s.rend()){// *it2 = 'x'; // 不能修改cout << *it2 << " ";++it2;}cout << endl
(3)cbegin

是C++11之后定义的,就是const版本的begin

7 查找算法

注意 使用算法时必须包含头文件   #include<algorithm>

三 auto和范围for


在这里补充2个C++11的小语法,方便我们后面的学习

auto关键字

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

2. 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

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

4. auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

5 . auto不能直接用来声明数组

// C++11int i = 0;// 通过初始化表达式值类型自动推荐对象类型auto j = i;auto k = 10;auto p1 = &i;// 指定一定是指针auto* p2 = &i;cout << p1 << endl;cout << p2 << endl;// 引用int& r1 = i;// r2不是int&引用,是intauto r2 = r1;//r3是int& 引用auto& r3 = r1;cout << &r2 << endl;cout << &r1 << endl;cout << &i << endl;cout << &r3 << endl;

范围for

1 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
2 范围for可以作用到数组和容器对象上进行遍历
3 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

// C++11// 范围for// 自动取容器数据赋值,自动迭代++,自动判断结束//for (auto ch : s1)for (char ch : s1){cout << ch << ' ';}cout << endl;for (auto e : lt){cout << e << ' ';}cout << endl;

for (auto ch : s1):就是把当前s1的值赋值给ch

范围for的特点:自动取容器数据赋值,自动迭代++,自动判断结束

支持迭代器的容器,都可以使用范围for

数组也支持(特殊处理)

​
int a[10] = { 1,2,3 };for (auto e : a){cout << e << " ";}cout << endl;
}​
  1. 根据 C++ 规则,未显式初始化的剩余元素会被自动初始化为 0,所以数组实际存储为 [1,2,3,0,0,0,0,0,0,0]

  2. 范围 for 循环for (auto e : a)遍历数组a所有 10 个元素(包括未显式初始化的 0),依次输出每个元素的值。

四 完整代码

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<string>
#include<algorithm>
#include<list>
using namespace std;//int main()
//{
//	// 净网行动
//	// 卧槽
//	// 10:48
//	char buff1[] = "abcA";
//	buff1[0]++;
//
//	char buff2[] = "比特abc";
//	cout << sizeof(buff2) << endl;
//	buff2[1]++;
//	cout << buff2 << endl;
//
//	buff2[1]++;
//	cout << buff2 << endl;
//
//	buff2[3]++;
//	cout << buff2 << endl;
//
//	buff2[3]++;
//	cout << buff2 << endl;
//
//	return 0;
//}void test_string1()
{string s1;string s2("hello world");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;string s4(s2, 0, 5);cout << s4 << endl;// pos位置拷贝结尾string s5(s2, 6, 15);cout << s5 << endl;string s6(s2, 6);cout << s6 << endl;string s7("hello world", 6);cout << s7 << endl;string s8(10, '*');cout << s8 << endl;s7 = "xxxx";cout << s7 << endl;
}//class string
//{
//public:
//	char& operator[] (size_t pos)
//	{
//		return _str[pos];
//	}
//private:
//	char* _str;
//	size_t _size;
//	size_t _capacity;
//};void Print(const string& s)
{//const string::iterator it1 = s.begin();string::const_iterator it1 = s.begin();while (it1 != s.end()){// *it1 = 'x'; // 不能修改cout << *it1 << " ";++it1;}cout << endl;string::const_reverse_iterator it2 = s.rbegin();while (it2 != s.rend()){// *it2 = 'x'; // 不能修改cout << *it2 << " ";++it2;}cout << endl;
}void test_string2()
{string s1("hello world");cout << s1 << endl;s1[0] = 'x';cout << s1 << endl;cout << s1[0] << endl;// 越界有严格断言检查//s1[12];  // 断言//s1.at(12); // 抛异常cout << s1.size() << endl; // 推荐cout << s1.length() << endl;// 下标+[]// 遍历 or 修改for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1 << endl;// 行为像指针一样的东西string::iterator it1 = s1.begin();while (it1 != s1.end()){// (*it1)--; 修改cout << *it1 << " ";++it1;}cout << endl;list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);list<int>::iterator lit = lt.begin();while (lit != lt.end()){cout << *lit << " ";++lit;}cout << endl;Print(s1);//string::iterator ret1 = find(s1.begin(), s1.end(), 'x');auto ret1 = find(s1.begin(), s1.end(), 'x');if (ret1 != s1.end()){cout<<"找到了x"<<endl;}//list<int>::iterator ret2 = find(lt.begin(), lt.end(), 2);auto ret2 = find(lt.begin(), lt.end(), 2);if (ret2 != lt.end()){cout << "找到了2" << endl;}// C++11int i = 0;// 通过初始化表达式值类型自动推荐对象类型auto j = i;auto k = 10;auto p1 = &i;// 指定一定是指针auto* p2 = &i;cout << p1 << endl;cout << p2 << endl;// 引用int& r1 = i;// r2不是int&引用,是intauto r2 = r1;//r3是int& 引用auto& r3 = r1;cout << &r2 << endl;cout << &r1 << endl;cout << &i << endl;cout << &r3 << endl;// C++11// 范围for// 自动取容器数据赋值,自动迭代++,自动判断结束//for (auto ch : s1)for (char ch : s1){cout << ch << ' ';}cout << endl;for (auto e : lt){cout << e << ' ';}cout << endl;
}int main()
{try{test_string2();}catch (const exception& e){cout << e.what() << endl;}return 0;
}

http://www.dtcms.com/a/407415.html

相关文章:

  • 浙江省建设厅官方网站移动互联网应用程序个人信息保护管理暂行规定(征求意见稿)
  • 兖州中材建设有限公司网站苏州的网络公司网站建设
  • 26X00.6588_GE_RELEASE_SVC_IM.250918-1932_CLIENT_IOT_LTSC_OEM_X64FRE_ZH-CN.iso
  • 旅游电子商务网站建设小百姓这个网站谁做的
  • Linux系统--进程信号
  • 门户网站盈利选服务好的佛山网站建设
  • 【开题答辩全过程】以 “物联网医院”-移动护理系统为例,包含答辩的问题和答案
  • 做网站的工作量怎么编辑网站内容
  • 基于STM32单片机的温湿度采集循迹避障APP小车
  • 单片机--概述
  • 文件与内容查找,压缩与解压
  • Emacs折腾日记(三十一)——org mode入门
  • 做网站推广的好处青岛市住房和城乡建设局官方网站
  • 科技网站域名北京顺义网站建设
  • 电子政务建设网站图片十大ppt模板免费下载网站
  • CentOS 7 安装并配置静态网络
  • 如何做网站使用手册济南网站定制策划
  • 什么网站可以做推广的宣传制作清单及价格
  • 龙海网站建设价格商城小程序开发哪家好
  • 厦门汽车充电站建设报备网站深圳浪尖工业设计公司
  • 创意交互设计广东短视频seo搜索哪家好
  • 亚马逊ImageSmith测试:搜索广告从“展示”到“对话”的革命
  • C语言程序设计笔记—scanf、算术运算符的使用案例
  • 旅行社手机网站建设方案wordpress tag 转拼音
  • 网站建设越来越注重用户体验网站开发职业资格证书
  • 基于Rokid平台的AR沉浸式教育导览应用:从构思到实现的全流程研究
  • 网站开发 足球球队信息做静态网站的软件
  • 青岛网站设计公司我国有哪些企业网站
  • 《用AI重构工业设备故障预警系统:从“被动维修”到“主动预判”的协作实践》
  • 建设部考试网站网站开发兼职网站