
目录
[1. 头文件](#1. 头文件)
[2. 标准库中的接口](#2. 标准库中的接口)
[3. auto与范围for](#3. auto与范围for)
[3.1 auto关键字](#3.1 auto关键字)
[3.2 范围for循环](#3.2 范围for循环)
[1. string类对象的常见构造](#1. string类对象的常见构造)
[2. string类对象的容量操作](#2. string类对象的容量操作)
[3. string类对象的访问及遍历操作](#3. string类对象的访问及遍历操作)
[4. string类对象的修改操作](#4. string类对象的修改操作)
[5. string类非成员函数](#5. string类非成员函数)
[6. vs和g++下string结构的说明](#6. vs和g++下string结构的说明)
[📝 本篇总结](#📝 本篇总结)
[🎯 下一篇预告](#🎯 下一篇预告)
前言
哈喽各位小伙伴们大家好呀,我还是你们的老朋友小小风呀!欢迎来到【从零开始学C++】专题的第七篇文章!🎉
在前六篇中,我们已经学习了C++的基础语法、类和对象等核心概念。今天我们要学习一个在实际开发中每天都会用到 的超级实用的类------string类!
相信学过C语言的同学都知道,C语言中的字符串操作有多麻烦:
-
要手动管理'\0'结束符
-
strcpy、strcat等函数容易越界
-
内存需要自己申请释放
-
各种坑等着你踩...
而C++的string类就是来拯救我们的!它封装了所有字符串操作,让我们像使用普通变量一样使用字符串,简单、安全又高效!
今天这篇文章我们就从最基础的开始,手把手带大家掌握string类的核心用法。新手小白也能轻松看懂,建议收藏慢慢看~
一、标准库中的string类
1. 头文件
基础概念解释
在C++中使用string类,就像使用其他工具一样,需要先"导入工具箱"。这个"工具箱"就是头文件。
使用string类必须满足两个条件:
-
包含头文件 :
#include <string>(注意不是<string.h>哦!那是C语言的) -
使用命名空间 :因为string在std命名空间中,所以需要
using namespace std;或者每次写std::string
💡 新手小贴士:很多新手容易写错成
#include <string.h>,这是C语言的字符串头文件,不是C++的string类!一定要注意区分!
2. 标准库中的接口
基础概念解释
string类是C++标准库中非常成熟的类,它提供了上百个接口函数。作为初学者,我们不需要全部记住,只需要掌握最常用的20%就能解决80%的问题。
当你遇到不熟悉的接口时,一定要学会查官方文档!这里给大家推荐最权威的C++参考文档:
👉 官方文档地址 :cplusplus.com/reference/string/string/
文档使用技巧:
-
左边是所有接口的目录,点击即可跳转
-
每个接口都有详细说明、参数、返回值、代码示例
-
支持中英文切换(右上角)
-
遇到不懂的先查文档,这是程序员必备技能!
💡 实用建议:收藏这个链接,写代码时随时查阅,比百度靠谱多了!
3. auto与范围for
在正式学习string之前,先给大家补充两个C++11的超级实用语法,这会让我们后面的代码简洁很多!
3.1 auto关键字
基础概念解释
在C++11之前,auto是用来声明自动变量的,基本没人用。C++11把它改造成了自动类型推导神器!
简单说:auto可以让编译器自动帮你判断变量的类型,你不用自己写了!
使用规则:
-
auto定义的变量必须初始化(不然编译器怎么推导类型?)
-
用于推导指针时,
auto和auto*效果一样 -
用于推导引用时,必须写
auto& -
同一行定义多个变量时,类型必须一致
适用场景:
-
类型名特别长的时候(比如迭代器)
-
你不确定类型是什么的时候
-
懒得写复杂类型名的时候
💡 新手小贴士:auto不是"动态类型",它是编译时推导的,推导完类型就固定了,和你手动写类型效果完全一样!
代码例子1:auto基础用法演示
cpp
#include <iostream>
#include <typeinfo> // 用于查看类型
using namespace std;
int main()
{
auto a = 10; // 自动推导为int
auto b = 3.14; // 自动推导为double
auto c = 'A'; // 自动推导为char
auto d = "hello"; // 自动推导为const char*
cout << "a的类型:" << typeid(a).name() << endl;
cout << "b的类型:" << typeid(b).name() << endl;
cout << "c的类型:" << typeid(c).name() << endl;
cout << "d的类型:" << typeid(d).name() << endl;
// auto e; // 错误!auto必须初始化!
return 0;
}
运行结果:
cpp
a的类型:int
b的类型:double
c的类型:char
d的类型:char const *
代码例子2:auto的真正威力------简化长类型
cpp
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
// 定义一个map,key是英文,value是中文
map<string, string> dict = {
{"apple", "苹果"},
{"orange", "橙子"},
{"pear", "梨"}
};
// 传统写法:类型名超级长!容易写错!
map<string, string>::iterator it1 = dict.begin();
// auto写法:太爽了!
auto it2 = dict.begin();
// 遍历输出
while (it2 != dict.end())
{
cout << it2->first << " -> " << it2->second << endl;
++it2;
}
return 0;
}
运行结果:
cpp
apple -> 苹果
orange -> 橙子
pear -> 梨
tops:map我们会在后续学习,这里只用来演示
3.2 范围for循环
基础概念解释
C++11又一个神级语法!专门用来遍历数组、容器等有范围的集合。
传统的for循环需要我们自己控制下标、判断边界,很容易写错。范围for直接帮你搞定一切:
-
自动迭代
-
自动取数据
-
自动判断结束
语法格式:
cpp
for (元素类型 变量名 : 要遍历的集合)
{
// 循环体
}
最佳实践:
-
只读遍历:用
auto e或const auto& e -
需要修改元素:用
auto& e(加引用!)
💡 新手小贴士:范围for的底层就是用迭代器实现的,所以支持迭代器的容器都能用范围for!
代码例子1:范围for遍历数组
cpp
#include <iostream>
using namespace std;
int main()
{
int arr[] = {1, 2, 3, 4, 5};
// 传统for循环:要算长度、控制下标
cout << "传统for:";
for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
{
cout << arr[i] << " ";
}
cout << endl;
// 范围for:就是这么简洁!
cout << "范围for:";
for (auto e : arr)
{
cout << e << " ";
}
cout << endl;
// 修改元素必须加引用!
for (auto& e : arr)
{
e *= 2; // 每个元素×2
}
cout << "修改后:";
for (auto e : arr)
{
cout << e << " ";
}
cout << endl;
return 0;
}
运行结果:
cpp
传统for:1 2 3 4 5
范围for:1 2 3 4 5
修改后:2 4 6 8 10
代码例子2:范围for遍历string
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "hello world";
// 用范围for遍历string的每个字符
cout << "逐个字符输出:";
for (auto ch : str)
{
cout << ch << "-";
}
cout << endl;
// 统计小写字母个数
int count = 0;
for (auto ch : str)
{
if (ch >= 'a' && ch <= 'z')
count++;
}
cout << "小写字母个数:" << count << endl;
return 0;
}
运行结果:
cpp
逐个字符输出:h-e-l-l-o- -w-o-r-l-d-
小写字母个数:10
二、string类的常用接口
1. string类对象的常见构造
基础概念解释
创建string对象的方式有很多,最常用的有4种:
|----------------------------|----------------|
| 构造函数 | 功能说明 |
| string() | 构造空字符串(最常用) |
| string(const char* s) | 用C风格字符串构造(最常用) |
| string(size_t n, char c) | 构造n个字符c组成的字符串 |
| string(const string& s) | 拷贝构造(最常用) |
💡 新手小贴士:前三个和拷贝构造是必须掌握的,其他的用到再查文档就行!
代码例子:四种构造函数的使用
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
// 1. 构造空字符串
string s1;
cout << "s1是空的吗?" << s1.empty() << endl;
cout << "s1的长度:" << s1.size() << endl;
// 2. 用C风格字符串构造(最常用!)
string s2("hello string!");
cout << "s2:" << s2 << endl;
// 3. 构造n个相同字符
string s3(10, 'x');
cout << "s3:" << s3 << endl;
// 4. 拷贝构造
string s4(s2);
cout << "s4:" << s4 << endl;
// 还有一种更简单的写法(隐式类型转换)
string s5 = "C++ yyds!";
cout << "s5:" << s5 << endl;
return 0;
}
运行结果:
cpp
s1是空的吗?1
s1的长度:0
s2:hello string!
s3:xxxxxxxxxx
s4:hello string!
s5:C++ yyds!
2. string类对象的容量操作
基础概念解释
string本质上是一个动态字符数组,所以它有容量相关的概念:
-
size/length:有效字符的个数(两个功能完全一样,推荐用size)
-
capacity:总共能存多少字符(不包括'\0')
-
empty:判断是否为空
-
clear:清空内容(不释放空间)
-
resize:改变有效字符个数
-
reserve:预留空间(避免频繁扩容)
重点注意:
-
size和length完全一样,只是为了和其他容器统一
-
clear只清数据,不改容量
-
reserve只改容量,不改有效数据个数
-
resize既改有效个数,也可能改容量
💡 实用技巧:如果你知道字符串大概多长,提前reserve可以大幅提升性能!
代码例子:容量操作演示
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("hello");
cout << "初始状态:" << endl;
cout << "s = " << s << endl;
cout << "size = " << s.size() << endl;
cout << "length = " << s.length() << endl;
cout << "capacity = " << s.capacity() << endl;
cout << "empty = " << s.empty() << endl;
cout << "-------------------" << endl;
// resize演示:扩大到10个字符,用'a'填充
s.resize(10, 'a');
cout << "resize(10, 'a')后:" << endl;
cout << "s = " << s << endl;
cout << "size = " << s.size() << endl;
cout << "capacity = " << s.capacity() << endl;
cout << "-------------------" << endl;
// resize演示:缩小到3个字符
s.resize(3);
cout << "resize(3)后:" << endl;
cout << "s = " << s << endl;
cout << "size = " << s.size() << endl;
cout << "capacity = " << s.capacity() << " (注意:容量没变!)" << endl;
cout << "-------------------" << endl;
// clear演示
s.clear();
cout << "clear后:" << endl;
cout << "s = " << s << endl;
cout << "size = " << s.size() << endl;
cout << "capacity = " << s.capacity() << " (注意:容量还是没变!)" << endl;
return 0;
}
运行结果:
cpp
初始状态:
s = hello
size = 5
length = 5
capacity = 15
empty = 0
-------------------
resize(10, 'a')后:
s = helloaaaaa
size = 10
capacity = 15
-------------------
resize(3)后:
s = hel
size = 3
capacity = 15 (注意:容量没变!)
-------------------
clear后:
s =
size = 0
capacity = 15 (注意:容量还是没变!)
3. string类对象的访问及遍历操作
基础概念解释
访问和遍历string有三种方式:
-
operator[]:像数组一样用下标访问(最常用)
-
迭代器:begin()和end()(通用方式,所有容器都支持)
-
范围for:C++11新语法(最简洁)
重点注意:
-
operator[]支持读写,越界会崩溃
-
迭代器是容器通用的遍历方式,学会了所有容器都能用
-
范围for底层就是迭代器
代码例子:三种遍历方式对比
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("abcdef");
// 方式1:operator[] 下标访问(最常用)
cout << "方式1:下标访问" << endl;
for (size_t i = 0; i < s.size(); i++)
{
cout << s[i] << " ";
s[i] += 1; // 可以修改
}
cout << endl << "修改后:" << s << endl << endl;
// 方式2:迭代器
cout << "方式2:迭代器" << endl;
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl << endl;
// 方式3:范围for(最简洁)
cout << "方式3:范围for" << endl;
for (auto ch : s)
{
cout << ch << " ";
}
cout << endl;
return 0;
}
运行结果:
cpp
方式1:下标访问
a b c d e f
修改后:bcdefg
方式2:迭代器
b c d e f g
方式3:范围for
b c d e f g
4. string类对象的修改操作
基础概念解释
string的修改操作是最常用的,核心有这几个:
-
push_back:尾插一个字符
-
append:尾插字符串
-
operator+=:尾插(最常用!字符、字符串、string都可以)
-
insert:任意位置插入
-
erase:删除字符
-
swap:交换两个string
-
c_str:转回C风格const char*(和C接口交互时用)
💡 重点推荐:90%的场景用operator+=就够了!它是最方便、最常用的!
代码例子1:尾插操作演示
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
// push_back:只能插一个字符
s.push_back('h');
s.push_back('e');
cout << "push_back后:" << s << endl;
// append:插字符串
s.append("llo");
cout << "append后:" << s << endl;
// operator+=:神器!什么都能插!
s += ' '; // 插字符
s += "world"; // 插字符串
s += s; // 插string对象
cout << "operator+=后:" << s << endl;
return 0;
}
运行结果:
cpp
push_back后:he
append后:hello
operator+=后:hello worldhello world
代码例子2:insert、erase、swap演示
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("hello world");
// insert:在第6个位置插入"C++ "
s.insert(6, "C++ ");
cout << "insert后:" << s << endl;
// erase:从第6个位置开始删除4个字符
s.erase(6, 4);
cout << "erase后:" << s << endl;
// swap:交换两个string(效率很高,只交换指针)
string s1("AAA");
string s2("BBB");
cout << "交换前:s1=" << s1 << ", s2=" << s2 << endl;
s1.swap(s2);
cout << "交换后:s1=" << s1 << ", s2=" << s2 << endl;
// c_str:和C语言接口交互时用
cout << "c_str():" << s.c_str() << endl;
return 0;
}
运行结果:
cpp
insert后:hello C++ world
erase后:hello world
交换前:s1=AAA, s2=BBB
交换后:s1=BBB, s2=AAA
c_str():hello world
5. string类非成员函数
基础概念解释
除了成员函数,string还有一些全局的非成员函数:
-
operator+:字符串拼接(尽量少用,会产生临时对象)
-
operator>> / operator<<:输入输出
-
getline:读取一行(重要!cin遇到空格就停了)
-
关系运算符:>、<、==、!=等(按字典序比较)
💡 超级重点:getline!读取带空格的字符串必须用它!比如读取"hello world"这种中间有空格的,用cin只能读到"hello"!
代码例子1:getline的重要性
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cout << "请输入一句话(包含空格):" << endl;
// cin的坑:遇到空格、回车、制表符就停止
// cin >> s; // 输入"hello world"只能读到"hello"
// getline:读到回车才停止,完美!
getline(cin, s);
cout << "你输入的是:" << s << endl;
cout << "长度是:" << s.size() << endl;
return 0;
}
运行示例:
cpp
请输入一句话(包含空格):
I love C++!
你输入的是:I love C++!
长度是:10
代码例子2:字符串比较
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("apple");
string s2("banana");
string s3("apple");
// 直接用比较运算符就行,太方便了!
cout << "s1 == s3:" << (s1 == s3) << endl;
cout << "s1 < s2:" << (s1 < s2) << endl; // 'a' < 'b'
cout << "s1 != s2:" << (s1 != s2) << endl;
return 0;
}
运行结果:
cpp
s1 == s3:1
s1 < s2:1
s1 != s2:1
6. vs和g++下string结构的说明
基础概念解释
这部分属于进阶内容,新手了解即可,面试可能会问到。
不同编译器的string底层实现是不一样的:
VS下的string(32位):
-
总共占28字节
-
采用短字符串优化(SSO):
-
长度<16时:存在内部的16字节数组里(不用开堆,快!)
-
长度≥16时:开堆空间存储
-
-
结构:16字节缓冲区 + 4字节size + 4字节capacity + 4字节其他 = 28字节
g++下的string(32位):
-
总共占4字节(只有一个指针!)
-
采用**写时拷贝(COW)**技术
-
指针指向堆空间,里面存:容量、长度、引用计数、字符串内容
💡 面试考点:短字符串优化(SSO)和写时拷贝(COW)的区别!
代码例子:验证string的大小
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "string对象的大小:" << sizeof(string) << "字节" << endl;
// VS下输出28,g++下输出4
// 大家可以在自己的编译器上试试!
string s1("123456789012345"); // 15个字符
string s2("1234567890123456"); // 16个字符
cout << "s1(15字符)的地址:" << (void*)s1.c_str() << endl;
cout << "s2(16字符)的地址:" << (void*)s2.c_str() << endl;
// VS下你会发现:
// s1的地址和对象地址很近(在栈上)
// s2的地址离对象很远(在堆上)
// 这就是短字符串优化!
return 0;
}
三、总结与预告
📝 本篇总结
今天我们系统学习了C++ string类的核心内容,重点掌握:
-
基础准备 :头文件
<string>+ 命名空间std -
实用语法:auto自动类型推导 + 范围for循环
-
四大核心操作:
-
构造:4种常用构造方式
-
容量:size、capacity、resize、reserve
-
遍历:下标、迭代器、范围for三种方式
-
修改:operator+=是神器!
-
-
重点注意:getline读取带空格的字符串
-
底层差异:VS的SSO vs g++的COW
string类是C++日常开发中使用频率最高的类没有之一,一定要多写多练,熟练掌握!
🎯 下一篇预告
下一篇文章我们将继续深入string类,讲解:
-
字符串的查找和截取(find、substr)
-
string类的模拟实现(面试必考!)
如果这篇文章对你有帮助,欢迎点赞👍 + 收藏⭐ + 评论💬 三连支持!
有任何问题都可以在评论区留言,我会一一回复~
关注我,带你从零开始系统学好C++!我们下篇再见!👋