1. stl简介
1.1 什么是stl
stl(standard template library , 标准模板库)是c++标准库的重要组成部分 , 它基于模板技术 , 提供了一系列通用的数据结构(容器)和算法 , 旨在提高代码的复用性 , 效率和标准化。
1.2 stl的发展
- 起源 : 20世纪80年代末 , 由alexander stepanov(亚历山大·斯特潘诺夫)等人提出 , 最初并非c++标准 , 后因实用性被纳入c++标准。
- 标准化 : 1998年 , stl正式成为c++98标准的一部分 , 后续在c++11 , c++14等版本中不断扩展(如增加无序容器 , 移动语义支持等)。
- 目标 : 通过模板实现“泛型编程” , 让数据结构和算法脱离具体数据类型 , 实现跨类型复用。
- 原始版本 : 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的重要性
- 提高开发效率 : 无需重复实现常见数据结构(如链表、哈希表)和算法(如排序) , 直接调用stl组件 , 减少代码量。
- 保证性能 : stl由专家优化 , 底层实现高效(如 vector 的连续内存 , map 的红黑树平衡操作)。
- 标准化 : 统一接口让代码更易读 , 易维护 , 不同开发者可基于stl达成共识。
- 泛型编程典范 : 展示了模板技术的强大 , 为c++后续发展奠定基础。
1.5 如何学习stl
- 1. 基础入门
- 先掌握c++模板语法(类模板 , 函数模板) , 理解stl的实现基础。
- 逐个学习核心容器 : vector (最常用)→ list → map / set → 无序容器 , 掌握其初始化 , 增删改查 , 迭代器使用。
- 2. 算法实践
- 熟悉 <algorithm> 头文件中的常用算法 : sort , find , count , reverse 等 , 结合容器练习。
- 理解算法的时间复杂度(如sort 是o(n log n) , find 是o(n))。
- 3. 深入原理
- 了解容器底层实现(如 vector 动态扩容机制 , map 红黑树结构) , 解释为何某些操作高效/低效。
- 学习迭代器失效场景(如 vector 插入元素可能导致迭代器失效)。
- 4. 实战应用
- 在编程题 , 项目中主动使用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 关系密切 , 且常被一起使用。
具体来说:
- 归属上 : std::string 是 c++ 标准库中专门用于处理字符串的类 , 定义在 <string> 头文件中 , 其设计早于 stl 标准化 , 核心目标是封装字符串操作(如拼接、比较、长度计算等) , 并非 stl 最初定义的“容器、算法、迭代器”体系的核心成员。
- 关联上 : std::string 支持 stl 的迭代器(可通过 begin() / end() 获取) , 能直接使用 stl 算法(如 std::sort 对字符串字符排序 , std::find 查找字符) , 且其内部实现也依赖模板技术 , 与 stl 的泛型思想一致。
2.3 为什么要学习string类
- c语言中 , 字符串是以 \0 结尾的一些字符的集合 , 为了操作方便 , c标准库中提供了一些str系列的库函数 , 但是这些库函数与字符串是分离开的 , 不太符合oop的思想 , 而且底层空间需要用户自己管理 , 稍不留神可能还会越界访问。
- 在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 显式指定):
#include <string> using namespace std; // 或使用 std::string
2. 字符串的初始化
- 默认初始化:创建空字符串。
string str1;
- 用字符串字面量初始化:
string str2 = "hello"; string str3("world");
- 用部分字符初始化 : string(const char* s, size_t n) , 从字符串 s 的开头取 n 个字符初始化。
string str4("abcdef", 3); // str4 为 "abc"
- 用重复字符初始化 : string(size_t n, char c) , 创建包含 n 个字符 c 的字符串。
string str5(5, 'a'); // str5 为 "aaaaa"
- 拷贝初始化 : 用另一个 string 对象初始化。
string str6 = str2; // str6 为 "hello"
3. 字符串的基本操作
(1) 长度与容量
- size() / length() : 获取字符串长度(字符个数) , 二者功能一致。
string str = "hello"; cout << str.size() << endl; // 输出 5 cout << str.length() << endl; // 输出 5
- capacity() :获取字符串当前已分配的内存能容纳的字符数(不包含结尾 \0 )。
cout << str.capacity() << endl;
- empty() :判断字符串是否为空 , 空返回 true , 否则返回 false。
if (str.empty()) { cout << "字符串为空" << endl; } else { cout << "字符串不为空" << endl; }
- resize(size_t n, char c = '\0') :调整字符串长度为 n , 若 n 大于原长度 , 新增字符用 c 填充 , 若 n 小于原长度 , 截断字符串。
str.resize(8, 'x'); // str 变为 "helloxxx" str.resize(3); // str 变为 "hel"
(2) 访问字符
- 下标访问( [] ):通过索引访问字符 , 类似数组 , 索引从 0 开始。
string str = "hello"; cout << str[0] << endl; // 输出 'h' str[1] = 'e'; // str 变为 "hello"
- 迭代器访问:string 支持迭代器 , 可用于遍历等操作。
for (string::iterator it = str.begin(); it != str.end(); ++it) { cout << *it << " "; }
- 也可结合 auto 简化
for (auto it = str.begin(); it != str.end(); ++it) { cout << *it << " "; }
- 范围 for 循环(c++11+) : 更简洁地遍历每个字符。
for (char c : str) { cout << c << " "; }
(3) 字符串拼接
- + 运算符:用于字符串与字符串 , 字符串与字符的拼接。
string str1 = "hello"; string str2 = " world"; string str3 = str1 + str2; // str3 为 "hello world" string str4 = str1 + '!'; // str4 为 "hello!"
- += 运算符:在原字符串后追加内容。
string str = "hello"; str += " world"; // str 为 "hello world" str += '!'; // str 为 "hello world!"
- append() 方法:功能类似 += , 也可追加部分字符。
str.append("test"); // 追加字符串 "test" str.append("abcdef", 3); // 追加 "abcdef" 的前 3 个字符 "abc"
(4) 字符串查找
- find(const string& str, size_t pos = 0) : 从位置 pos 开始查找子串 str , 返回第一次出现的起始索引 , 若未找到返回 string::npos (一个很大的无符号整数)。
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 ,回最后一次出现的起始索引。
size_t pos = str.rfind("hello"); // 返回 12
- find_first_of(const string& str, size_t pos = 0) : 从位置 pos 开始 , 查找 str 中任意一个字符第一次出现的索引。
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 中任意一个字符最后一次出现的索引。
size_t pos = str.find_last_of("aeiou"); // 返回 7('o' 的索引)
(5) 字符串截取与替换
- substr(size_t pos = 0, size_t len = npos) :从位置 pos 开始 , 截取长度为 len 的子串 , 若 len 为 npos 则截取到字符串末尾。
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。
string str = "hello world"; str.replace(6, 5, "c++"); // str 变为 "hello c++"
(6) 字符串比较
- 关系运算符( == 、 != 、 < 、 > 、 <= 、 >= ):按字典序比较两个字符串。
string str1 = "abc"; string str2 = "abd"; if (str1 < str2) { cout << "str1 小于 str2" << endl; }
- compare(const string& str) :比较当前字符串与 str , 返回值:
- 小于 0:当前字符串小于 str;
- 等于 0:两者相等;
- 大于 0:当前字符串大于 str。
int res = str1.compare(str2); if (res < 0) { cout << "str1 < str2" << endl; }
(7) 字符串的插入与删除
- insert(size_t pos, const string& str) :在位置 pos 插入字符串 str。
string str = "hello"; str.insert(5, " world"); // str 变为 "hello world"
- erase(size_t pos = 0, size_t len = npos) :从位置 pos 开始 , 删除长度为 len 的子串 , 若 len 为 npos 则删除到字符串末尾。
string str = "hello world"; str.erase(5, 6); // 删除从索引 5 开始的 6 个字符,str 变为 "hello" str.erase(2); // 删除从索引 2 开始到末尾的字符,str 变为 "he"
4. 其他常用操作
- 清空字符串: clear() , 将字符串变为空。
string str = "hello"; str.clear();
- 交换字符串: swap(string& str) , 交换两个字符串的内容。
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 结束。
语法形式:
char& string::operator[](size_t pos); // 非 const 版本,可修改字符 const char& string::operator[](size_t pos) const; // const 版本,仅读取
特点与使用场景:
- - 优点 : 语法简洁 , 像操作数组一样直观 , 访问效率高(直接通过索引定位内存)。
- - 缺点:
- - 不做范围检查 , 若 pos 超出 [0, size()-1] , 会导致未定义行为(如程序崩溃、乱码等)。
- - 只能访问单个字符 , 遍历所有字符时需手动控制索引。
示例:
#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 迭代器等)。
迭代器类型:
- string::iterator :正向迭代器,支持读写操作。 - string::const_iterator :const 正向迭代器,仅支持读操作(用于 const string )。 - string::reverse_iterator :反向迭代器,从后往前遍历。 - string::const_reverse_iterator :const 反向迭代器。
常用迭代器方法
- begin()/cbegin() :返回指向字符串首字符的迭代器(cbegin() 返回 const_iterator )。 - end()/cend() :返回指向字符串**末尾(最后一个字符的下一个位置)**的迭代器。 - rbegin()/crbegin() :返回指向字符串最后一个字符的反向迭代器。 - rend()/crend() :返回指向字符串首字符前一个位置的反向迭代器。
特点与使用场景:
- - 优点:
- - 通用:符合 stl 迭代器规范 , 可与 stl 算法(如 std::sort , std::find )无缝配合。
- - 安全:遍历逻辑由迭代器封装 , 不易因索引错误导致越界。
- - 灵活:支持正向、反向等多种遍历方式。
- - 缺点:语法比下标访问复杂 , 新手可能需要适应。
示例
#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 容器)或数组的所有元素,语法非常简洁。
语法形式:
for (元素类型 变量名 : 容器/数组) { // 操作变量名 }
特点与使用场景:
- - 优点:
- - 语法极简:无需手动控制索引或迭代器, 直接遍历所有元素。
- - 安全:由编译器保证遍历范围的正确性 , 不会越界。
- - 缺点:
- - 仅支持顺序遍历 , 无法灵活控制遍历方向(如反向)。
- - 若需要修改字符 , 需注意是否用引用(否则是值拷贝 , 修改不影响原字符串)。
示例:
#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 等类型名。
示例:
#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 中元素的类型,进一步简化代码。
示例:
#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类需掌握模板语法、核心容器使用、算法实践及底层原理 , 建议通过实际项目应用加深理解。
到此这篇关于c++ string类详解(stl简介, string类,访问修改字符)的文章就介绍到这了,更多相关c++ string类内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论