
🏝️专栏: 【C++修炼之路】
🌅主页: f狐o狸x
"不为盛名而来,不为低估而去"
hello!大家好久不见呀~从今天开始我将继续整理关于C++的学习心得,欢迎大家一起学习
在 C 语言中,我们处理字符串依赖于字符数组和一系列 C 标准库函数(如strcpy、strcat、strcmp),不仅需要手动管理内存,还容易出现缓冲区溢出等问题。而 C++ 标准库提供的std::string(简称string),封装了字符串的所有操作,提供了安全、便捷、高效的字符串处理方案,是 C++ 开发中处理字符串的首选。
本文将从基础到实用,全面讲解string的核心用法,适合 C++ 初学者快速上手,也可作为日常开发的参考手册。
一、前期准备:使用 string 的前提
要使用string,必须满足两个基本条件(缺一不可):
- 包含
string头文件(注意无后缀.h) - 引用
std命名空间(string是标准库中的类,定义在std命名空间下)
cpp
#include <iostream>
// 1. 包含string头文件
#include <string>
// 2. 引用std命名空间(两种方式二选一)
// 方式1:全局引用(简洁,适合小程序/示例代码)
using namespace std;
// 方式2:局部引用(更安全,适合大型项目,避免命名冲突)
// 后续使用时写 std::string 即可
int main() {
// 后续string操作写在这里
return 0;
}
二、string 的基本初始化:多种创建方式
string提供了多种初始化方式,满足不同场景的需求,以下是最常用的几种:
2.1. 空字符串初始化
创建一个长度为 0 的空字符串,后续可通过赋值、拼接等操作填充内容。
cpp
// 两种等价写法
string str1; // 默认构造函数,空字符串
string str2 = ""; // 赋值空字符串常量
2.2. 直接用字符串常量初始化
最常用的方式,直接将双引号包裹的字符串常量赋值给string对象。
cpp
string str3 = "Hello, C++ string!"; // 拷贝初始化
string str4("Welcome to C++ world"); // 直接初始化(构造函数传参)
2.3. 用另一个 string 对象初始化
复制已有string对象的内容,创建一个新的字符串对象。
cpp
string str5 = str3; // 拷贝初始化,str5与str3内容完全一致
string str6(str4); // 直接初始化,str6与str4内容完全一致
2.4. 用指定字符和长度初始化
创建一个由n个相同字符组成的字符串。
cpp
int n = 5;
char ch = 'a';
string str7(n, ch); // str7 = "aaaaa"
2.5. 截取其他字符串的部分内容初始化
从已有字符串的指定索引开始,截取指定长度的内容(后续substr方法会详细讲解)。
cpp
string str8 = "Hello, World!";
string str9(str8, 7, 5); // 从索引7开始,截取5个字符,str9 = "World"
三、string 的核心基础操作
3.1. 获取字符串长度 / 判断是否为空
string提供了两种获取长度的方法,功能完全一致,还有一个专门判断是否为空的高效方法:
size():返回字符串的字符个数(推荐,语义更清晰,与其他 STL 容器保持一致)length():返回字符串的字符个数(历史遗留方法,与size()等价)empty():判断字符串是否为空,为空返回true,否则返回false(比size() == 0更高效)
示例代码:
cpp
string str = "Hello, C++";
cout << "字符串长度(size()):" << str.size() << endl;
cout << "字符串长度(length()):" << str.length() << endl;
if (str.empty()) {
cout << "str 是空字符串" << endl;
} else {
cout << "str 不是空字符串" << endl;
}
// 清空字符串后再判断
string empty_str;
if (empty_str.empty()) {
cout << "empty_str 是空字符串" << endl;
}
运行结果:

3.2. 字符串的访问:下标 [] vs at ()
string支持两种访问单个字符的方式,均通过索引获取:
(1)下标运算符 []
- 语法:
str[index] - 特点:不做越界检查,若索引超出范围(
index >= str.size()或index < 0),会导致未定义行为(程序可能崩溃、输出乱码等) - 适用场景:确定索引不会越界时(如循环遍历),效率更高
(2)at() 成员方法
- 语法:
str.at(index) - 特点:会做越界检查,若索引超出范围,会抛出
std::out_of_range异常 - 适用场景:索引可能不确定(如用户输入、动态计算),需要安全校验时
示例代码:
cpp
string str = "Hello";
// 1. 下标[]访问
cout << "通过[]访问第0个字符:" << str[0] << endl;
cout << "通过[]访问第3个字符:" << str[3] << endl;
// 2. at()方法访问
cout << "通过at()访问第1个字符:" << str.at(1) << endl;
cout << "通过at()访问第4个字符:" << str.at(4) << endl;
// 3. 循环遍历所有字符(两种方式均可)
cout << "循环遍历字符串([]):";
for (int i = 0; i < str.size(); ++i) {
cout << str[i] << " ";
}
cout << endl;
// 4. 越界访问演示(注释掉,避免程序崩溃)
// str[10]; // 未定义行为,可能崩溃
// str.at(10); // 抛出out_of_range异常
运行结果:

3.3. 字符串的修改与拼接
string提供了多种便捷的修改和拼接方式,告别 C 语言的strcat、strcpy。
(1)直接赋值(覆盖原有内容)
使用=运算符,直接将新字符串覆盖原有字符串的内容。
cpp
string str = "Old string";
cout << "赋值前:" << str << endl;
str = "New string"; // 直接赋值,覆盖原有内容
cout << "赋值后:" << str << endl;
(2)拼接操作(追加内容,不覆盖原有内容)
- 方式 1:
+=运算符(简洁高效,推荐日常使用),支持拼接string对象、字符串常量、单个字符 - 方式 2:
append()方法(功能更强大,支持拼接部分字符串、多个重复字符等)
示例代码:
cpp
string str1 = "Hello";
string str2 = " World";
// 1. += 运算符拼接
str1 += str2; // 拼接string对象
cout << "拼接后1:" << str1 << endl;
str1 += "!"; // 拼接字符串常量
cout << "拼接后2:" << str1 << endl;
str1 += '@'; // 拼接单个字符
cout << "拼接后3:" << str1 << endl;
// 2. append() 方法拼接
string str3 = "I love";
str3.append(" C++"); // 拼接字符串常量
cout << "append拼接后1:" << str3 << endl;
str3.append(3, '!'); // 拼接3个重复字符
cout << "append拼接后2:" << str3 << endl;
str3.append(str1, 0, 5); // 拼接str1的前5个字符(从索引0开始,长度5)
cout << "append拼接后3:" << str3 << endl;
运行结果:

3.4. 字符串的比较
string支持直接使用比较运算符进行字符串比较,也提供了compare()方法,比较规则与 C 语言的strcmp一致:按字符的 ASCII 码值逐字符比较,直到出现不同字符或到达字符串末尾。
(1)比较运算符(推荐,简洁直观)
支持的运算符:==(相等)、!=(不相等)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)
(2)compare() 方法(功能更强大,返回整数值)
- 返回值:0(两个字符串相等)、正数(当前字符串大于目标字符串)、负数(当前字符串小于目标字符串)
示例代码:
cpp
string str1 = "Apple";
string str2 = "Banana";
string str3 = "Apple";
// 1. 比较运算符
if (str1 == str3) {
cout << "str1 与 str3 相等" << endl;
}
if (str1 < str2) {
cout << "str1 小于 str2" << endl;
}
if (str2 != str1) {
cout << "str2 与 str1 不相等" << endl;
}
// 2. compare() 方法
int result = str1.compare(str2);
if (result < 0) {
cout << "str1.compare(str2) 返回负数,str1 < str2" << endl;
}
result = str1.compare(str3);
if (result == 0) {
cout << "str1.compare(str3) 返回0,str1 == str3" << endl;
}
// 比较部分字符串(str1从索引0开始,长度3 与 str2从索引0开始,长度3比较)
result = str1.compare(0, 3, str2, 0, 3);
cout << "str1前3个字符与str2前3个字符比较结果:" << result << endl;
运行结果:

四、string 的常用实用操作
4.1. 字符串查找:find ()
find() 方法用于在当前字符串中查找指定的子串或字符,返回首次出现的索引 ;若查找失败,返回string::npos(一个静态常量,代表无效索引,通常被定义为-1,但实际是无符号整数类型)。
语法(常用):
str.find(substr):查找子串substr首次出现的索引str.find(ch):查找字符ch首次出现的索引str.find(substr, pos):从索引pos开始,查找子串substr首次出现的索引
示例代码:
cpp
string str = "Hello, World! Hello, C++!";
string substr = "Hello";
char ch = 'W';
// 1. 查找子串首次出现的索引
size_t index1 = str.find(substr);
if (index1 != string::npos) {
cout << "子串 \"" << substr << "\" 首次出现的索引:" << index1 << endl;
}
// 2. 查找字符首次出现的索引
size_t index2 = str.find(ch);
if (index2 != string::npos) {
cout << "字符 \'" << ch << "\' 首次出现的索引:" << index2 << endl;
}
// 3. 从指定索引开始查找子串
size_t index3 = str.find(substr, 7);
if (index3 != string::npos) {
cout << "从索引7开始,子串 \"" << substr << "\" 首次出现的索引:" << index3 << endl;
}
// 4. 查找不存在的子串
size_t index4 = str.find("Java");
if (index4 == string::npos) {
cout << "未找到子串 \"Java\"" << endl;
}
运行结果:

注意:
find()的返回值类型是size_t(无符号整数类型),不要用int接收,避免负数转换问题。
4.2. 字符串截取:substr ()
substr() 方法用于截取字符串的一部分,返回一个新的string对象,不会修改原字符串。
语法:
str.substr(pos, len):从索引pos开始,截取长度为len的字符串- 若
len省略,或pos + len超出字符串长度,则截取到字符串末尾
示例代码:
cpp
string str = "Hello, World! Hello, C++!";
// 1. 截取指定长度的字符串
string sub1 = str.substr(7, 5); // 从索引7开始,截取5个字符
cout << "截取结果1:" << sub1 << endl;
// 2. 截取到字符串末尾
string sub2 = str.substr(19); // 从索引19开始,截取到末尾
cout << "截取结果2:" << sub2 << endl;
// 3. 索引超出范围(抛出out_of_range异常)
// string sub3 = str.substr(100);
运行结果:

4.3. 字符串替换:replace ()
replace() 方法用于将字符串中的指定部分替换为新的字符串,会修改原字符串。
语法(常用):
str.replace(pos, len, new_str):从索引pos开始,将长度为len的字符串替换为new_str
示例代码:
cpp
string str = "I love Java!";
cout << "替换前:" << str << endl;
// 将从索引7开始的4个字符("Java")替换为"C++"
str.replace(7, 4, "C++");
cout << "替换后:" << str << endl;
// 替换为更长的字符串
str.replace(7, 3, "C++ Programming");
cout << "再次替换后:" << str << endl;
运行结果:

4.4. 清空字符串:clear ()
clear() 方法用于清空字符串的所有内容,清空后字符串变为空字符串,size()返回 0。
示例代码:
cpp
string str = "Hello, C++!";
cout << "清空前长度:" << str.size() << endl;
str.clear();
cout << "清空后长度:" << str.size() << endl;
cout << "是否为空:" << (str.empty() ? "是" : "否") << endl;
运行结果:

五、string 与 C 语言字符数组的转换
在实际开发中,我们可能需要与 C 语言接口交互(如某些函数要求传入const char*类型参数),此时需要进行string与 C 语言字符数组的转换。
5.1. string 转 const char*(两种方法)
c_str():返回一个以\0结尾的const char*(C 风格字符串),是最常用的方法data():C++11 及以后,data()与c_str()功能一致,均返回const char*;C++11 之前,data()不保证返回的字符串以\0结尾
注意:
- 返回的
const char*是只读的,不能修改其内容- 其有效期与原
string对象一致,若原string对象被修改(如赋值、拼接)或销毁,返回的指针将失效
示例代码:
cpp
string str = "Hello, C language!";
// 1. c_str() 转换
const char* c_str1 = str.c_str();
cout << "c_str() 转换结果:" << c_str1 << endl;
// 2. data() 转换
const char* c_str2 = str.data();
cout << "data() 转换结果:" << c_str2 << endl;
// 3. 注意:修改原字符串后,指针失效
str = "Modified string";
// cout << c_str1 << endl; // 未定义行为,输出结果不可预期
运行结果:

5.2. const char* 转 string
直接赋值即可,非常简洁,string会自动处理\0结尾,无需手动管理长度。
示例代码:
cpp
const char* c_str = "Hello, C++ string!";
// 直接赋值转换
string str = c_str;
cout << "转换后的string:" << str << endl;
// 也可以在初始化时直接赋值
string str2(c_str);
cout << "初始化转换后的string:" << str2 << endl;
运行结果:

六、完整可运行示例代码
将以上知识点整合为一个完整程序,可直接复制编译运行:
cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
// 1. 初始化
string str = "Hello, C++!";
string str_copy = str;
string str_char(5, 'a');
// 2. 输出基本信息
cout << "原始字符串:" << str << endl;
cout << "拷贝字符串:" << str_copy << endl;
cout << "字符填充字符串:" << str_char << endl;
cout << "字符串长度:" << str.size() << endl;
cout << "是否为空:" << (str.empty() ? "是" : "否") << endl;
// 3. 访问与修改
cout << "第0个字符:" << str[0] << endl;
str[0] = 'h'; // 小写化第一个字符
cout << "修改后字符串:" << str << endl;
// 4. 拼接
str += " Welcome!";
cout << "拼接后字符串:" << str << endl;
// 5. 查找与截取
size_t index = str.find("Welcome");
if (index != string::npos) {
string sub = str.substr(index);
cout << "找到的子串:" << sub << endl;
}
// 6. 替换
str.replace(index, 7, "Hello World");
cout << "替换后字符串:" << str << endl;
// 7. 转换为C风格字符串
const char* c_str = str.c_str();
cout << "C风格字符串:" << c_str << endl;
// 8. 清空
str.clear();
cout << "清空后字符串长度:" << str.size() << endl;
return 0;
}
运行结果:

七、模拟实现string
下面是我根据string的主要功能模拟实现的部分string的函数,大家可以参考一下
7.1. string.h
cpp
#define _CRT_SECURE_NO_WARNINGS 1;
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
namespace fox
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
// 构造函数
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// 析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
const char* c_str() const
{
return _str;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
size_t size()
{
return _size;
}
size_t capacity()
{
return _capacity;
}
char& operator[](size_t pos)
{
//检查pos合法性
assert(pos < _size);
return *(_str + pos);
}
const char& operator[](size_t pos) const
{
//检查pos合法性
assert(pos < _size);
return *(_str + pos);
}
void reserve(size_t n);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos = 0, size_t len = npos);
private:
char* _str; // string指针
size_t _size; // 大小
size_t _capacity; // 容量
static const size_t npos;
};
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}
7.2. string.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1;
#include "string.h"
using namespace std;
namespace fox
{
const size_t string::npos = -1;
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);//二倍扩容
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
}
strcpy(_str + _size, str);
_size += len;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);//二倍扩容
}
//挪动数据
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
_size++;
}
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size == _capacity)
{
reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
}
//挪动数据
size_t end = _size +len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
for (int i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i < _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
assert(pos <= _size);
for (size_t i = pos; i < _size; i++)
{
if (ch == _str[i])
return i;
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
// len大于剩余字符长度,更新一下len
if (len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
bool operator<(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{
return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{
return !(s1 < s2);
}
bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
const int N = 256;
char buff[N];
int i = 0;
char ch;
//in >> ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
//in >> ch;
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
void TestString1()
{
fox::string s1("hello!");
cout << s1 << endl;
fox::string s2;
cin >> s2;
cout << s2 << endl;
}
int main()
{
TestString1();
return 0;
}
八、总结
string是 C++ 标准库的字符串类,使用前需包含<string>头文件并引用std命名空间。- 初始化方式灵活,支持空字符串、常量赋值、对象拷贝等多种形式。
- 基础操作:
size()/length()获取长度、empty()判断为空、[]/at()访问字符、+=/append()拼接、比较运算符进行比较。 - 实用操作:
find()查找、substr()截取、replace()替换、clear()清空。 - 与 C 语言字符数组转换:
c_str()/data()转const char*,直接赋值实现const char*转string。
进阶拓展
string的内存管理:string会自动管理内存,动态扩容,也可通过reserve()预分配内存,提升效率。- 迭代器遍历:
string支持 STL 迭代器,可用于遍历、排序等操作。 - 与算法库结合:可使用
<algorithm>中的函数(如sort()、reverse())操作string。 - 宽字符字符串:
wstring用于处理中文、日文等宽字符,用法与string基本一致。
掌握string的基本用法,能满足绝大多数日常开发的字符串处理需求,后续可深入学习其进阶特性,进一步提升 C++ 字符串处理的效率和灵活性。