string类的使用
- 一、auto 关键字:简化类型声明的利器
- 二、string 类:更安全的字符串处理方案
- 1、常见构造
- 2、容量操作
- 3、访问及遍历操作
- 4、string 类的其他常用操作
- 三、常用遍历
- 1、C++11遍历
- 2、迭代器遍历
- 3、for遍历
- 四、string 类的实现原理(进阶)
- 传统写法
- 现代写法
- 五、string 类的实际应用场景
一、auto 关键字:简化类型声明的利器
- 自动类型推导:无需显式指定类型,编译器根据初始化值确定变量类型
- 简化复杂声明:对于迭代器、函数返回值等冗长类型,
auto能大幅精简代码- 指针与引用规则:
- 声明指针时,
auto与auto*效果一致(如auto p = &a与auto* p = &a等价)- 声明引用必须显式添加
&(如auto& ref = a)- 多变量声明限制:同一行声明的多个变量必须为相同类型(如
auto a = 1, b = 2合法,auto a = 1, b = 2.0非法)- 使用限制:
- 必须在声明时初始化(无法单独声明
auto x;)- 不能作为函数参数类型(如
void func(auto x)不合法)- 不建议作为函数返回值类型(可能导致类型模糊)
- 不能用于声明数组(如
auto arr[10]不合法)
#include<iostream>#include<typeinfo>// 需包含此头文件以使用 typeidusingnamespacestd;intgetNum(){return42;}intmain(){inta=10;autob=a;// 推导为 intautoc='a';// 推导为 charautod=getNum();// 推导为 intauto*p=&a;// 推导为 int*(与 auto p = &a 等价)auto&ref=a;// 推导为 int&// 打印推导的类型(不同编译器输出可能略有差异)cout<<"b 的类型:"<<typeid(b).name()<<endl;// intcout<<"c 的类型:"<<typeid(c).name()<<endl;// charcout<<"d 的类型:"<<typeid(d).name()<<endl;// intcout<<"p 的类型:"<<typeid(p).name()<<endl;// int*return0;}二、string 类:更安全的字符串处理方案
C++ 标准库的string类封装了字符串的创建、修改和操作,相比 C 风格字符数组(char*),提供了自动内存管理和丰富的成员函数,是字符串处理的首选。
核心优势
- 自动内存管理:无需手动
malloc/free或new/delete,避免内存泄漏- 安全性:内置越界检查,减少缓冲区溢出风险
- 丰富接口:提供拼接、查找、截取等便捷操作
- 兼容性:支持与 C 风格字符串(
const char*)相互转换- 动态扩展:可自动调整容量以适应字符串长度变化
1、常见构造
| constructor函数名称 | 功能说明 |
|---|---|
| string() | 构造空字符串(默认构造) |
| string(const char* s) | 用 C 风格字符串初始化 |
| string(const string&s) | 拷贝构造(复制已有 string 对象) |
| string(size_t n, char c) | 构造包含 n 个字符 c 的字符串构造包含n个字符c的string类对象 |
#include<string>usingnamespacestd;intmain(){string s1;// 空字符串strings2("hello");// 用 C 字符串初始化strings3(s2);// 拷贝构造strings4(5,'!');// 5个感叹号:"!!!!!"cout<<"s2: "<<s2<<endl;// 输出:hellocout<<"s4: "<<s4<<endl;// 输出:!!!!!return0;}2、容量操作
| 函数名称 | 功能说明 |
|---|---|
| size | 返回有效字符长度(推荐使用,替代length()) |
| empty | 判断字符串是否为空(为空返回true) |
| clear | 清空有效字符(不释放底层内存) |
| reserve | 预留至少 n 个字符的空间(提升插入效率) |
| resize(n, c) | 调整有效字符数为 n,多余位置用 c 填充 |
- 一般情况下都是用
size(),而不是length()
2.clear()只是将string中有效字符清空,不改变底层空间大小
3.resize(n)与resize(n, c)的区别:
- 前者将多余位置初始化为空字符(’\0’)
- 后者将多余位置初始化为指定字符 c
- 若n大于当前容量,会扩容;若n小于当前长度,仅截断不缩容
4.reserve(n)仅预留空间,不改变有效字符数,n 小于当前容量时不做操作
#include<iostream>usingnamespacestd;intmain(){strings1("hello world");cout<<s1.size()<<endl;//11cout<<s1.empty()<<endl;//0s1.clear();cout<<s1<<endl;//打印空string s2;s2.reserve(100);cout<<s2.capacity()<<endl;//111//左(要初始化个数):右(初始化的字符)s2.resize(4,'c');cout<<s2<<endl;//ccccreturn0;}3、访问及遍历操作
| 方式 | 适用场景 |
|---|---|
| operator[] | 通过下标访问(支持读写,类似数组) |
| begin+end | 正向迭代器,从首到尾遍历(适用于所有 STL 容器),begin()指向首字符,end()指向尾字符后一位 |
| rbegin+rend | 反向迭代器,从尾到首遍历,rbegin()指向尾字符,rend()指向首字符前一位 |
| 范围for | C++11 新增,简洁遍历所有元素(底层基于迭代器) |
#include<iostream>#include<string>usingnamespacestd;intmain(){strings("hello");// 1. 下标访问cout<<"第2个字符:"<<s[1]<<endl;// 'e's[0]='H';// 修改首字符为'H'cout<<"修改后:"<<s<<endl;// Hello// 2. 正向迭代器cout<<"正向遍历:";for(autoit=s.begin();it!=s.end();++it){cout<<*it<<" ";// H e l l o}cout<<endl;// 3. 反向迭代器cout<<"反向遍历:";for(autoit=s.rbegin();it!=s.rend();++it){cout<<*it<<" ";// o l l e H}cout<<endl;// 4. 范围 for(C++11)cout<<"范围for遍历:";for(charch:s){cout<<ch<<" ";// H e l l o}cout<<endl;return0;}4、string 类的其他常用操作
| 函数名称 | 功能说明 |
|---|---|
| operator+= | 字符串拼接(支持 string 或 C 字符串) |
| append() | 尾部追加字符串(功能类似+=) |
| c_str() | 返回 C 风格字符串(const char*) |
| find(sub, pos) | 从 pos 位置开始查找子串 sub,返回起始索引(未找到返回string::npos) |
| substr(pos, len) | 从 pos 位置截取长度为 len 的子串(默认截取到末尾) |
| compare() | 比较与字符串 s 的大小(返回 0 表示相等) |
#include<iostream>#include<string>usingnamespacestd;intmain(){string s1="hello";string s2="world";// 字符串拼接s1+=" ";s1+=s2;cout<<s1<<endl;// 输出:hello world// 查找操作size_t pos=s1.find("world");if(pos!=string::npos){cout<<"找到子串,位置:"<<pos<<endl;// 输出:6}// 截取子串string s3=s1.substr(6,5);cout<<s3<<endl;// 输出:world// C风格字符串转换constchar*cstr=s1.c_str();cout<<cstr<<endl;// 输出:hello worldreturn0;}三、常用遍历
1、C++11遍历
适用于数组和支持下标访问的容器(如 string、vector),需要手动控制索引范围。
#include<iostream>#include<string>usingnamespacestd;intmain(){//C++98遍历intarray1[]={1,2,3,4,5};for(inti=0;i<sizeof(array1)/sizeof(array1[0]);++i){array1[i]*=2;}for(inti=0;i<sizeof(array1)/sizeof(array1[0]);++i){cout<<array1[i]<<" ";}cout<<endl;return0;}2、迭代器遍历
迭代器是 STL 容器的通用遍历方式,适用于所有容器(包括不支持下标访问的容器如 list、map 等)。
![[QQ20251203-213626.png]]
#include<iostream>#include<string>usingnamespacestd;intmain(){strings1("hello world");//正向迭代器//string::iterator it = s1.begin();autoit=s1.begin();while(it!=s1.end()){cout<<*it<<" ";++it;}cout<<endl;//反向迭代器//string::reverse_iterator rit = s1.rbegin();autorit=s1.rbegin();while(rit!=s1.rend()){cout<<*rit<<" ";++rit;}cout<<endl;conststrings2("hello world");//const正向迭代器//string::const_iterator cit = s2.begin();autocit=s2.begin();while(cit!=s2.end()){cout<<*cit<<" ";++cit;}cout<<endl;//const反向迭代器//string::const_reverse_iterator rcit = s2.rbegin();autorcit=s2.rbegin();while(rcit!=s2.rend()){cout<<*rcit<<" ";++rcit;}return0;}3、for遍历
一种简洁的遍历方式,自动迭代容器中所有元素,底层基于迭代器实现。语法格式:
for(元素类型 变量名:容器名){// 循环体}#include<iostream>#include<string>usingnamespacestd;intmain(){// 数组遍历intarray2[]={1,2,3,4,5};for(auto&e:array2)// 使用引用避免拷贝,支持修改元素e*=2;for(autoe:array2)cout<<e<<" ";// 输出:2 4 6 8 10cout<<endl;// 字符串遍历stringstr("hello world");for(autoch:str){cout<<ch<<" ";// 输出:h e l l o w o r l d}cout<<endl;return0;}四、string 类的实现原理(进阶)
了解 string 类的实现有助于更好地理解其特性,下面展示两种经典的实现方式:
传统写法
通过显式分配和释放内存实现深拷贝,确保每个对象拥有独立的字符串资源:
classString{public:// 构造函数String(constchar*str=""){if(nullptr==str){assert(false);return;}_str=newchar[strlen(str)+1];// 分配空间(包含结束符)strcpy(_str,str);// 拷贝内容}// 拷贝构造函数String(constString&s):_str(newchar[strlen(s._str)+1]){strcpy(_str,s._str);}// 赋值运算符重载String&operator=(constString&s){if(this!=&s)// 避免自赋值{char*pStr=newchar[strlen(s._str)+1];strcpy(pStr,s._str);delete[]_str;// 释放旧空间_str=pStr;// 指向新空间}return*this;}// 析构函数~String(){if(_str){delete[]_str;_str=nullptr;}}private:char*_str;// 存储字符串};现代写法
通过交换临时对象的资源简化代码,利用临时对象的生命周期自动释放内存
classString{public:// 构造函数String(constchar*str=""){if(nullptr==str){assert(false);return;}_str=newchar[strlen(str)+1];strcpy(_str,str);}// 拷贝构造函数(现代写法)String(constString&s):_str(nullptr){StringstrTmp(s._str);// 创建临时对象swap(_str,strTmp._str);// 交换资源}// 赋值运算符重载(现代写法)String&operator=(String s)// 传值参数会触发拷贝构造{swap(_str,s._str);// 交换资源,临时对象会自动释放旧资源return*this;}// 析构函数~String(){if(_str){delete[]_str;_str=nullptr;}}private:char*_str;};五、string 类的实际应用场景
- 文本处理:日志记录、配置文件解析、字符串格式化
- 用户交互:命令行输入输出、GUI 文本控件
- 网络编程:HTTP 协议处理、数据报文组装与解析
- 文件操作:路径处理、文件内容读写
- 数据转换:数值与字符串的相互转换
掌握auto和string是 C++ 开发的基础,合理使用能显著提升代码简洁性和安全性。实际开发中,建议优先使用标准库提供的string而非 C 风格字符串,减少内存管理风险。