C++ string类(STL简介 , string类 , 访问修改字符)

目录

[1. STL简介](#1. STL简介)

[1.1 什么是STL](#1.1 什么是STL)

[1.2 STL的发展](#1.2 STL的发展)

[1.3 STL的六大组件](#1.3 STL的六大组件)

[1.4 STL的重要性](#1.4 STL的重要性)

[1.5 如何学习STL](#1.5 如何学习STL)

[2. string类](#2. string类)

[2.1 string类的概念](#2.1 string类的概念)

[2.2 string类与STL的关系:](#2.2 string类与STL的关系:)

[2.3 为什么要学习string类](#2.3 为什么要学习string类)

[3. 标准库中的string类](#3. 标准库中的string类)

[3.1 string类](#3.1 string类)

[3.2 基本语法](#3.2 基本语法)

[3.3 访问和修改字符](#3.3 访问和修改字符)

[1. 下标访问( [] 运算符)](#1. 下标访问( [] 运算符))

[2. 迭代器访问](#2. 迭代器访问)

[3. 范围for循环(C++11及以上)](#3. 范围for循环(C++11及以上))

[4. 结合 auto 的遍历](#4. 结合 auto 的遍历)

[4. 总结:](#4. 总结:)


1. STL简介

1.1 什么是STL

STL(Standard Template Library , 标准模板库)是C++标准库的重要组成部分 , 它基于模板技术 , 提供了一系列通用的数据结构(容器)和算法 , 旨在提高代码的复用性 , 效率和标准化。

1.2 STL的发展

  1. 起源 : 20世纪80年代末 , 由Alexander Stepanov(亚历山大·斯特潘诺夫)等人提出 , 最初并非C++标准 , 后因实用性被纳入C++标准。
  2. 标准化 : 1998年 , STL正式成为C++98标准的一部分 , 后续在C++11 , C++14等版本中不断扩展(如增加无序容器 , 移动语义支持等)。
  3. 目标 : 通过模板实现"泛型编程" , 让数据结构和算法脱离具体数据类型 , 实现跨类型复用。
  • 原始版本: Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本 , 本着开源精神 , 他们声明允许任何人任意运用 , 拷贝 , 修改 , 传播 , 商业使用这些代码 , 无需付费。唯一的条件就是也需要向原始版本一样做开源使用。HP版本 - 所有STL实现版本的始祖。
  • P.J.版本 : 由P.J. Plauger开发 , 继承自HP版本 , 被Windows Visual C++采用 , 不能公开或修改 , 缺陷 : 可读性比较低 , 符号命名比较怪异。
  • RW版本 : 由Rouge Wage公司开发 , 继承自HP版本 , 被C++ Builder 采用 , 不能公开或修改 , 可读性一般。
  • SGI版本: 由Silicon Graphics Computer Systems, Inc公司开发 , 继承自HP版本。被GCC(Linux)采用 , 可移植性好 , 可公开 , 修改甚至贩卖 , 从命名风格和编程风格上看 , 阅读性非常高。我们后面学习STL要阅读部分源代码 , 主要参考的就是这个版本。

1.3 STL的六大组件

1.4 STL的重要性

  1. 提高开发效率 : 无需重复实现常见数据结构(如链表、哈希表)和算法(如排序) , 直接调用STL组件 , 减少代码量。
  2. 保证性能 : STL由专家优化 , 底层实现高效(如 vector 的连续内存 , map 的红黑树平衡操作)。
  3. 标准化 : 统一接口让代码更易读 , 易维护 , 不同开发者可基于STL达成共识。
  4. 泛型编程典范 : 展示了模板技术的强大 , 为C++后续发展奠定基础。

1.5 如何学习STL

    1. 基础入门
  • 先掌握C++模板语法(类模板 , 函数模板) , 理解STL的实现基础。
  • 逐个学习核心容器 : vector (最常用)→ list → map / set → 无序容器 , 掌握其初始化 , 增删改查 , 迭代器使用。
    1. 算法实践
  • 熟悉 <algorithm> 头文件中的常用算法 : sort , find , count , reverse 等 , 结合容器练习。
  • 理解算法的时间复杂度(如sort 是O(n log n) , find 是O(n))。
    1. 深入原理
  • 了解容器底层实现(如 vector 动态扩容机制 , map 红黑树结构) , 解释为何某些操作高效/低效。
  • 学习迭代器失效场景(如 vector 插入元素可能导致迭代器失效)。
    1. 实战应用
  • 在编程题 , 项目中主动使用STL , 例如用 unordered_map 做哈希映射 , vector 存储动态数组。
  • 对比不同容器的适用场景(如需要随机访问用 vector , 频繁插入删除用 list)。

2. string类

2.1 string类的概念

简单说 , std::string 是标准库中独立的字符串工具 , 但因适配 STL 的迭代器和算法 , 常被视为"准 STL 组件" , 在实际开发中与 STL 容器(如 vector<string> )配合频繁。

2.2 string类与STL的关系:

std::string 不属于 STL 的核心组成部分 , 但它与 STL 关系密切 , 且常被一起使用。

具体来说:

  1. 归属上 : std::string 是 C++ 标准库中专门用于处理字符串的类 , 定义在 <string> 头文件中 , 其设计早于 STL 标准化 , 核心目标是封装字符串操作(如拼接、比较、长度计算等) , 并非 STL 最初定义的"容器、算法、迭代器"体系的核心成员。
  2. 关联上 : std::string 支持 STL 的迭代器(可通过 begin() / end() 获取) , 能直接使用 STL 算法(如 std::sort 对字符串字符排序 , std::find 查找字符) , 且其内部实现也依赖模板技术 , 与 STL 的泛型思想一致。

2.3 为什么要学习string类

  1. C语言中 , 字符串是以 \0 结尾的一些字符的集合 , 为了操作方便 , C标准库中提供了一些str系列的库函数 , 但是这些库函数与字符串是分离开的 , 不太符合OOP的思想 , 而且底层空间需要用户自己管理 , 稍不留神可能还会越界访问。
  2. 在OJ中 , 有关字符串的题目基本以string类的形式出现 , 而且在常规工作中 , 为了简单、方便、快捷 , 基本都使用string类 , 很少有人去使用C库中的字符串操作函数。

3. 标准库中的string类

3.1 string类

std::string 是 C++ 标准库中用于处理字符串的类 , 定义在 <string> 头文件中 , 属于 std 命名空间。使用 string 类时 , 需包含头文件 <string> , 并通常通过 using namespace std; (或指定 std::string) 来使用。它封装了字符串的存储与操作 , 无需像 C 语言字符数组那样手动管理内存(如扩容 , 越界等问题)。

3.2 基本语法

1. 包含头文件与命名空间

要使用 string 类 , 需包含头文件 <string> , 且通常使用 std 命名空间(也可通过 std::string 显式指定):

cpp 复制代码
#include <string>
using namespace std; // 或使用 std::string

2. 字符串的初始化

- 默认初始化:创建空字符串。

cpp 复制代码
string str1;
  • 用字符串字面量初始化:
cpp 复制代码
string str2 = "hello";
string str3("world");

- 用部分字符初始化 : string(const char* s, size_t n) , 从字符串 s 的开头取 n 个字符初始化。

cpp 复制代码
string str4("abcdef", 3); // str4 为 "abc"
  • 用重复字符初始化: string(size_t n, char c) , 创建包含 n 个字符 c 的字符串。
cpp 复制代码
string str5(5, 'a'); // str5 为 "aaaaa"

- 拷贝初始化 : 用另一个 string 对象初始化。

cpp 复制代码
string str6 = str2; // str6 为 "hello"

3. 字符串的基本操作

(1) 长度与容量

  • size() / length() : 获取字符串长度(字符个数) , 二者功能一致。
cpp 复制代码
string str = "hello";
cout << str.size() << endl;    // 输出 5
cout << str.length() << endl;  // 输出 5
  • capacity() :获取字符串当前已分配的内存能容纳的字符数(不包含结尾 \0 )。
cpp 复制代码
cout << str.capacity() << endl;
  • empty() :判断字符串是否为空 , 空返回 true , 否则返回 false。
cpp 复制代码
if (str.empty()) 
{
    cout << "字符串为空" << endl;
} else 
{
    cout << "字符串不为空" << endl;
}
  • resize(size_t n, char c = '\0') :调整字符串长度为 n , 若 n 大于原长度 , 新增字符用 c 填充 , 若 n 小于原长度 , 截断字符串。
cpp 复制代码
str.resize(8, 'x'); // str 变为 "helloxxx"
str.resize(3);      // str 变为 "hel"

(2) 访问字符

  • 下标访问( [] ):通过索引访问字符 , 类似数组 , 索引从 0 开始。
cpp 复制代码
string str = "hello";
cout << str[0] << endl; // 输出 'h'
str[1] = 'E';           // str 变为 "hEllo"

- 迭代器访问:string 支持迭代器 , 可用于遍历等操作。

cpp 复制代码
for (string::iterator it = str.begin(); it != str.end(); ++it) 
{
    cout << *it << " ";
}
  • 也可结合 auto 简化
cpp 复制代码
for (auto it = str.begin(); it != str.end(); ++it) 
{
    cout << *it << " ";
}
  • 范围 for 循环(C++11+) : 更简洁地遍历每个字符。
cpp 复制代码
for (char c : str) 
{
    cout << c << " ";
}

(3) 字符串拼接

  • + 运算符:用于字符串与字符串 , 字符串与字符的拼接。
cpp 复制代码
string str1 = "hello";
string str2 = " world";
string str3 = str1 + str2; // str3 为 "hello world"
string str4 = str1 + '!';  // str4 为 "hello!"
  • += 运算符:在原字符串后追加内容。
cpp 复制代码
string str = "hello";
str += " world"; // str 为 "hello world"
str += '!';      // str 为 "hello world!"
  • append() 方法:功能类似 += , 也可追加部分字符。
cpp 复制代码
str.append("test"); // 追加字符串 "test"
str.append("abcdef", 3); // 追加 "abcdef" 的前 3 个字符 "abc"

(4) 字符串查找

- find(const string& str, size_t pos = 0) : 从位置 pos 开始查找子串 str , 返回第一次出现的起始索引 , 若未找到返回 string::npos (一个很大的无符号整数)。

cpp 复制代码
string str = "hello world hello";
size_t pos = str.find("hello"); // 找到,返回 0
pos = str.find("hello", 1);     // 从索引 1 开始找,返回 12
if (str.find("test") == string::npos) 
{
    cout << "未找到子串" << endl;
}

- rfind(const string& str, size_t pos = npos): 从位置 pos 开始反向查找子串 str ,回最后一次出现的起始索引。

cpp 复制代码
size_t pos = str.rfind("hello"); // 返回 12

- find_first_of(const string& str, size_t pos = 0): 从位置 pos 开始 , 查找 str 中任意一个字符第一次出现的索引。

cpp 复制代码
string str = "hello world";
size_t pos = str.find_first_of("aeiou"); // 找元音字母,返回 1('e' 的索引)

- find_last_of(const string& str, size_t pos = npos):从位置 pos 开始 , 查找 str 中任意一个字符最后一次出现的索引。

cpp 复制代码
size_t pos = str.find_last_of("aeiou"); // 返回 7('o' 的索引)

(5) 字符串截取与替换

  • substr(size_t pos = 0, size_t len = npos):从位置 pos 开始 , 截取长度为 len 的子串 , 若 len 为 npos 则截取到字符串末尾。
cpp 复制代码
string str = "hello world";
string sub = str.substr(6, 5); // sub 为 "world"
sub = str.substr(0, 5);        // sub 为 "hello"
sub = str.substr(6);           // sub 为 "world"
  • replace(size_t pos, size_t len, const string& str):从位置 pos 开始 , 替换长度为 len 的子串为 str。
cpp 复制代码
string str = "hello world";
str.replace(6, 5, "C++"); // str 变为 "hello C++"

(6) 字符串比较

- 关系运算符( == 、 != 、 < 、 > 、 <= 、 >= ):按字典序比较两个字符串。

cpp 复制代码
string str1 = "abc";
string str2 = "abd";
if (str1 < str2) 
{
    cout << "str1 小于 str2" << endl;
}

- compare(const string& str) :比较当前字符串与 str , 返回值:

  • 小于 0:当前字符串小于 str;

  • 等于 0:两者相等;

  • 大于 0:当前字符串大于 str。

cpp 复制代码
int res = str1.compare(str2);
if (res < 0)
{
    cout << "str1 < str2" << endl;
}

(7) 字符串的插入与删除

  • insert(size_t pos, const string& str):在位置 pos 插入字符串 str。
cpp 复制代码
string str = "hello";
str.insert(5, " world"); // str 变为 "hello world"
  • erase(size_t pos = 0, size_t len = npos) :从位置 pos 开始 , 删除长度为 len 的子串 , 若 len 为 npos 则删除到字符串末尾。
cpp 复制代码
string str = "hello world";
str.erase(5, 6); // 删除从索引 5 开始的 6 个字符,str 变为 "hello"
str.erase(2);    // 删除从索引 2 开始到末尾的字符,str 变为 "he"

4. 其他常用操作

- 清空字符串: clear() , 将字符串变为空。

cpp 复制代码
string str = "hello";
str.clear();

- 交换字符串: swap(string& str) , 交换两个字符串的内容。

cpp 复制代码
string str1 = "hello";
string str2 = "world";
str1.swap(str2); // str1 变为 "world",str2 变为 "hello"

掌握这些 string 类的语法和操作 , 能满足大部分字符串处理的需求 , 让 C++ 中的字符串操作更加高效和简洁。

3.3 访问和修改字符

在 C++ 中 , 访问 std::string 字符主要有下标访问、迭代器访问、范围 for 循环、结合 auto 的遍历这几种方式 , 下面分别详细讲解:

1. 下标访问( [] 运算符)

std::string 重载了 [] 运算符,支持像数组一样通过索引访问单个字符,索引从 0 开始,到 size() - 1 结束。

语法形式:

cpp 复制代码
char& string::operator[](size_t pos);         // 非 const 版本,可修改字符
const char& string::operator[](size_t pos) const; // const 版本,仅读取

特点与使用场景:

    • 优点 : 语法简洁 , 像操作数组一样直观 , 访问效率高(直接通过索引定位内存)。
    • 缺点:
    • 不做范围检查 , 若 pos 超出 [0, size()-1] , 会导致未定义行为(如程序崩溃、乱码等)。
    • 只能访问单个字符 , 遍历所有字符时需手动控制索引。

示例:

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

int main() 
{
    string str = "hello";
    // 访问单个字符
    cout << str[0] << endl; // 输出 'h'
    // 修改字符
    str[1] = 'E';
    cout << str << endl; // 输出 "hEllo"

    // 遍历所有字符(需手动控制索引)
    for (size_t i = 0; i < str.size(); ++i) 
    {
        cout << str[i] << " ";
    }
    // 输出:h E l l o
    return 0;
}

2. 迭代器访问

迭代器是 STL 中连接容器和算法的"桥梁" , std::string 提供了迭代器来遍历字符 , 支持多种迭代器类型(如正向迭代器、反向迭代器、const 迭代器等)。

迭代器类型:

cpp 复制代码
-  string::iterator :正向迭代器,支持读写操作。
-  string::const_iterator :const 正向迭代器,仅支持读操作(用于  const string )。
-  string::reverse_iterator :反向迭代器,从后往前遍历。
-  string::const_reverse_iterator :const 反向迭代器。

常用迭代器方法

cpp 复制代码
-  begin()/cbegin() :返回指向字符串首字符的迭代器(cbegin() 返回 const_iterator )。
-  end()/cend() :返回指向字符串**末尾(最后一个字符的下一个位置)**的迭代器。
-  rbegin()/crbegin() :返回指向字符串最后一个字符的反向迭代器。
-  rend()/crend() :返回指向字符串首字符前一个位置的反向迭代器。

特点与使用场景:

    • 优点:
    • 通用:符合 STL 迭代器规范 , 可与 STL 算法(如 std::sort , std::find )无缝配合。
    • 安全:遍历逻辑由迭代器封装 , 不易因索引错误导致越界。
    • 灵活:支持正向、反向等多种遍历方式。
    • 缺点:语法比下标访问复杂 , 新手可能需要适应。

示例

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

int main() 
{
    string str = "hello";

    // 正向迭代器(读写)
    for (string::iterator it = str.begin(); it != str.end(); ++it) 
    {
        *it = toupper(*it); // 转大写
    }
    cout << str << endl; // 输出 "HELLO"

    // const 正向迭代器(只读,用于 const string)
    const string constStr = "world";
    for (string::const_iterator cit = constStr.cbegin(); cit != constStr.cend(); ++cit)
    {
        cout << *cit << " ";
    }
    // 输出:w o r l d

    // 反向迭代器
    for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit)
    {
        cout << *rit << " ";
    }
    // 输出:O L L E H
    return 0;
}

3. 范围for循环(C++11及以上)

范围 for 是 C++11 引入的语法糖 , 专门用于遍历容器(如 std::string , STL 容器)或数组的所有元素,语法非常简洁。

语法形式:

cpp 复制代码
 
for (元素类型 变量名 : 容器/数组) 
{
    // 操作变量名
}
 

特点与使用场景:

    • 优点:
    • 语法极简:无需手动控制索引或迭代器, 直接遍历所有元素。
    • 安全:由编译器保证遍历范围的正确性 , 不会越界。
    • 缺点:
    • 仅支持顺序遍历 , 无法灵活控制遍历方向(如反向)。
    • 若需要修改字符 , 需注意是否用引用(否则是值拷贝 , 修改不影响原字符串)。

示例:

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

int main() 
{
    string str = "hello";

    // 只读遍历(值拷贝)
    for (char c : str) 
    {
        cout << c << " ";
    }
    // 输出:h e l l o

    // 读写遍历(引用)
    for (char& c : str) 
    {
        c = toupper(c); // 转大写,修改原字符串
    }
    cout << str << endl; // 输出 "HELLO"

    // const 字符串的只读遍历
    const string constStr = "world";
    for (char c : constStr) 
    {
        cout << c << " ";
    }
    // 输出:w o r l d
    return 0;
}

4. 结合 auto 的遍历

auto 是 C++11 引入的类型推导关键字 , 可让编译器自动推导变量类型。结合迭代器或范围 for 时 , 能进一步简化代码。

与迭代器结合 : 利用 auto 推导迭代器类型 , 避免写冗长的 string::iterator 等类型名。

示例:

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

int main() 
{
    string str = "hello";

    // 正向迭代器(auto 推导类型)
    for (auto it = str.begin(); it != str.end(); ++it) 
    {
        *it = toupper(*it);
    }
    cout << str << endl; // 输出 "HELLO"

    // 反向迭代器(auto 推导类型)
    for (auto rit = str.rbegin(); rit != str.rend(); ++rit) 
    {
        cout << *rit << " ";
    }
    // 输出:O L L E H
    return 0;
}

与范围 for 结合 : 用 auto 推导范围 for 中元素的类型,进一步简化代码。

示例:

cpp 复制代码
#include <string>
#include <iostream>
using namespace std;

int main() 
{
    string str = "hello";

    // 只读遍历(auto 推导为 char)
    for (auto c : str) 
    {
        cout << c << " ";
    }
    // 输出:h e l l o

    // 读写遍历(auto& 推导为 char&)
    for (auto& c : str)
    {
        c = toupper(c);
    }
    cout << str << endl; // 输出 "HELLO"

    // const 字符串的只读遍历(auto 推导为 char)
    const string constStr = "world";
    for (auto c : constStr)
    {
        cout << c << " ";
    }
    // 输出:w o r l d
    return 0;
}

总结:四种方式的对比与选择

实际开发中 , 建议优先使用范围 for + auto(简洁且安全);若需复杂遍历(如反向、与 STL 算法配合) , 则用迭代器 + auto;仅需随机访问单个字符时 , 再考虑下标访问。

4. 总结:

STL(标准模板库)是C++标准库的重要组成部分 , 提供通用数据结构和算法 , 基于模板技术实现泛型编程。STL包含六大组件, , 如容器、算法、迭代器等 , 能提高开发效率、保证性能并促进代码标准化。string类虽不属于STL核心 , 但常与STL配合使用 , 提供强大的字符串操作功能 , 包括初始化、访问、修改、查找、拼接等。学习STL和string类需掌握模板语法、核心容器使用、算法实践及底层原理 , 建议通过实际项目应用加深理解。

感谢大家的观看!

相关推荐
-Xie-8 分钟前
Maven(二)
java·开发语言·maven
mftang10 分钟前
Python可视化工具-Bokeh:动态显示数据
开发语言·python
m0_4805026420 分钟前
Rust 入门 生命周期-next2 (十九)
开发语言·后端·rust
IT利刃出鞘20 分钟前
Java线程的6种状态和JVM状态打印
java·开发语言·jvm
忒可君2 小时前
C# winform FTP功能
开发语言·windows·c#
Forward♞2 小时前
Qt——文件操作
开发语言·c++·qt
十五年专注C++开发3 小时前
CMake进阶: CMake Modules---简化CMake配置的利器
linux·c++·windows·cmake·自动化构建
老虎06273 小时前
JavaWeb前端02(JavaScript)
开发语言·前端·javascript