C++从入门到实战(十五)String(上)介绍STL与String的关系,为什么有string类,String有什么用
C++从入门到实战(十五)String(上)
- 前言
- 一、STL与String的关系
- 1. STL 是什么?
- 2. String 是什么?
- 3. String 与 STL 的关系
- 二、为什么有string类,有什么用
- 1. 为什么需要 string 类?
- 2. string 类的核心作用:
- 3. string 类的其他能力:(后面会详细讲,现在看一下就好)
- 4. 和 C风格字符串对比
- 三、auto和范围for
- 1. auto 关键字:
- 1.1 基本用法
- 1.2 细节注意
- 1.3 与Python的对比
- 2. 范围for循环:
- 2.1 基本语法
- 2.2 修改元素:用引用
- 3. 适用范围
- (1)适用场景
- 1. STL容器(如 vector、list、map 等)
- 2. 普通数组(静态/动态)
- 3. 字符串(`std::string` 或 C风格字符串)
- 4. 初始化列表(临时构造的集合)
- (2)不适用场景(了解就好)
- 1. 需要索引时
- 2. 动态修改容器大小
- 3. 通过指针访问的数组
- 4. 不支持迭代器的自定义类型
- (3)关键细节
- 1. 修改元素:用引用 &
- 2. 避免拷贝:用 const auto&
- 3. 底层原理
前言
- 在前面的博客中,我们已经探讨了 C++ 的类和对象、内存管理、模板等重要概念。
- 从本篇开始,我们将进入 C++ 的另一个重要阶段 —— 标准模板库(STL)的学习。STL 是 C++ 强大的工具集,它提供了各种容器、算法和迭代器,极大地提高了代码的复用性和开发效率。
- 在上一篇博客中,我们介绍了 STL 的基本概念和组成部分。这篇博客,我们将着重介绍STL与String的关系,为什么有string类,String有什么用。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
一、STL与String的关系
一句话总结:string是 STL 提供的、专门用来高效处理字符串的 “容器类”,既具备 STL 容器的通用特性(迭代器、动态内存等),又针对字符串操作做了专属优化
1. STL 是什么?
STL是C++标准库的核心组件,提供了一套通用的模板类和函数,用于处理各种数据结构(如数组、链表、栈、队列等)和算法(如排序、查找等)。
类比:STL就像一个“工具箱”,里面有各种“工具”(容器、算法、迭代器等),可以直接拿来用。
2. String 是什么?
- String是STL中的一个容器类,专门用于处理字符串(文本数据)。它封装了字符数组,提供了更方便的操作方法(如拼接、查找、替换等)。
- 类比:String就像“工具箱”里的“螺丝刀”,是专门用来拧螺丝(处理字符串)的工具。
3. String 与 STL 的关系
- String 是 STL 容器的一种:STL包含多种容器(如
vector
、list
、map
),而string
是其中专门处理文本的容器。 - 共享 STL 的特性:
- 动态管理内存:无需手动分配/释放内存,如
string s = "hello";
。 - 支持迭代器:可以像遍历数组一样遍历字符串(如
s.begin()
、s.end()
)。 - 丰富的方法:如
s.length()
、s.substr()
、s.find()
等。
- 动态管理内存:无需手动分配/释放内存,如
二、为什么有string类,有什么用
1. 为什么需要 string 类?
生活场景:
假设你是老师,要记录学生的姓名。如果没有 string
,你得用 C风格字符数组(像用便签纸记录):
char name[100]; // 必须提前指定最大长度(比如最多100个字符)
但现实中,名字长度不一(有人叫"张三",有人叫"阿卜杜勒·拉赫曼"),用固定长度的数组会遇到两个问题:
- 问题1:长度不够(比如名字超过100个字符),数组会越界(像便签纸写不下,字会挤到外面)。
- 问题2:长度浪费(比如名字只有3个字符,却占了100个位置)。
而 string
类就像一个自动伸缩的便签纸,你写多少字,它就自动扩展多长,完美解决上述问题!
2. string 类的核心作用:
#include <string> // 使用string必须包含这个头文件std::string name = "阿卜杜勒·拉赫曼"; // 无需指定长度,自动适应内容
- 优势1:无需关心内存大小,随便存多长的字符串。
- 优势2:无需手动释放内存,用起来更安全(不会忘记释放导致内存泄漏)。
3. string 类的其他能力:(后面会详细讲,现在看一下就好)
生活场景:
假设你要给学生分组,需要把姓和名拼起来(比如"张" + “三” → “张三”),或者判断名字是否包含"小"字。
用 string
类,这些操作就像玩积木一样简单:
std::string first = "张";
std::string last = "三";// 1. 拼接字符串(积木拼接)
std::string fullName = first + last; // "张三"// 2. 获取长度(量积木长度)
int len = fullName.length(); // 2// 3. 判断是否包含子串(找积木中的特定小块)
bool hasXiao = fullName.find("小") != std::string::npos; // false// 4. 替换部分内容(替换积木的某一块)
fullName.replace(0, 1, "李"); // "张三" → "李三"// 5. 截取子串(从积木中拆出一部分)
std::string sub = fullName.substr(0, 1); // "李"
4. 和 C风格字符串对比
操作 | C风格字符数组 (char[] ) | C++ string 类 |
---|---|---|
初始化 | char name[10] = "张三"; | std::string name = "张三"; |
拼接 | strcat(name, "三"); (需确保数组足够大) | name += "三"; (自动扩展) |
获取长度 | strlen(name); (需遍历数组) | name.length(); (O(1) 时间复杂度) |
复制 | strcpy(dest, src); (需手动管理内存) | dest = src; (自动处理) |
安全性 | 容易越界,导致程序崩溃 | 自动管理内存,不会越界 |
总结
-
自动伸缩:不用操心内存大小,随便存多长的内容。
-
操作简单:拼接、查找、替换等功能直接用,像搭积木一样方便。
-
安全可靠:不会越界,不会忘记释放内存,程序更稳定。
-
以后写代码遇到字符串,直接用
string
!除非特殊场景(如性能要求极高、和C语言交互),否则完全不需要用char[]
。
三、auto和范围for
在正式开始之前我们需要详细讲解auto和范围for,以帮助我们后续的理解string
1. auto 关键字:
生活场景:
假设你是餐厅服务员,客人点了一杯“饮料”(没说具体是什么)。你不用纠结,直接根据客人的语气和场景猜:
- 如果客人说“来杯冰的!” → 可能是“冰可乐”。
- 如果客人说“要热的!” → 可能是“热茶”。
auto
就像这个“猜饮料”的过程,让编译器根据右边的值自动推导变量的类型。
1.1 基本用法
auto num = 42; // 编译器猜:int
auto pi = 3.14; // 编译器猜:double
auto name = "Alice"; // 编译器猜:const char*
优势:
- 代码更简洁,不用写冗长的类型名(比如
std::vector<int>::iterator
)。 - 避免手动写错类型。
1.2 细节注意
-
指针与引用:
int x = 10; auto ptr = &x; // auto 推导为 int*(auto* 也可以,但没必要) auto& ref = x; // 必须加 &,否则 ref 会是 int(复制值)
-
同一行声明多个变量:
auto a = 1, b = 2; // 正确:都是 int auto c = 3, d = 3.14; // 错误:c 是 int,d 是 double
-
不能做函数参数:
void func(auto x) { } // 错误:编译器无法推导参数类型
1.3 与Python的对比
特性 | C++ auto | Python 变量 |
---|---|---|
类型推导时机 | 编译时(静态类型) | 运行时(动态类型) |
类型是否固定 | 一旦推导,类型固定 | 运行中可以改变 |
示例 | auto x = 1; x = "hello"; // 错误 | x = 1; x = "hello"; // 正确 |
2. 范围for循环:
生活场景:
假设你要挨个检查教室里的桌子,传统写法是:
- 从第一张桌子开始(初始化)。
- 检查当前桌子,然后走到下一张(迭代)。
- 直到走完所有桌子(终止条件)。
而范围for循环就像:“你直接说‘检查所有桌子’,系统自动帮你挨个检查,不用操心细节”。
2.1 基本语法
// 字符串数组std::string words[3] = {"apple", "banana", "cherry"};// 传统for循环std::cout << "传统for循环: ";for (size_t i = 0; i < 3; i++) {std::cout << words[i] << " ";}// 范围for循环std::cout << "\n范围for循环: ";for (const auto& word : words) {std::cout << word << " ";}
2.2 修改元素:用引用
// 使用引用(&)遍历并修改每个字符串for (auto& word : words) {word += "!"; // 给每个字符串添加感叹号}// 输出修改后的字符串for (const auto& word : words) {std::cout << word << " ";}
3. 适用范围
(1)适用场景
1. STL容器(如 vector、list、map 等)
#include <vector>
#include <map>
#include <iostream>int main() {// 遍历 vectorstd::vector<int> nums = {1, 2, 3};for (auto num : nums) { // 自动推导为 intstd::cout << num << " ";}// 遍历 map(键值对)std::map<std::string, int> scores = {{"Alice", 90}, {"Bob", 85}};for (const auto& pair : scores) { // pair 是 std::pair<const string, int>std::cout << pair.first << ": " << pair.second << "\n";}
}
2. 普通数组(静态/动态)
// 静态数组
int arr[] = {1, 2, 3};
for (auto x : arr) { ... }// 动态数组(通过指针访问时**不适用**,见下文注意事项)
int* dyn_arr = new int[3]{1, 2, 3};
// for (auto x : dyn_arr) { ... } // 错误!无法推导数组大小
3. 字符串(std::string
或 C风格字符串)
#include <string>std::string str = "hello";
for (char c : str) { // 遍历每个字符std::cout << c << " ";
}// C风格字符串(以 '\0' 结尾)
const char* c_str = "world";
for (char c : std::string(c_str)) { ... } // 需要转为 std::string
4. 初始化列表(临时构造的集合)
for (int x : {1, 3, 5, 7}) { // 直接遍历花括号初始化列表std::cout << x << " ";
}
(2)不适用场景(了解就好)
1. 需要索引时
范围for循环隐藏了迭代器/索引,无法直接获取当前元素的位置。
替代方案:用传统for循环或 std::views::enumerate
(C++20)。
2. 动态修改容器大小
遍历时添加/删除元素可能导致迭代器失效(如 vector
扩容)。
替代方案:用传统for循环,手动控制迭代器。
3. 通过指针访问的数组
int* ptr = arr; // arr 是普通数组
// for (auto x : ptr) { ... } // 错误!无法推导数组大小
4. 不支持迭代器的自定义类型
范围for依赖 begin()
和 end()
方法,若类型未实现则无法使用。
(3)关键细节
1. 修改元素:用引用 &
std::vector<int> nums = {1, 2, 3};
for (auto& num : nums) { // 引用传递,修改原数组num *= 2;
}
2. 避免拷贝:用 const auto&
遍历大对象(如 vector<string>
)时,值传递会拷贝对象,用引用更高效:
for (const auto& str : strs) { // 避免拷贝 string...
}
3. 底层原理
范围for循环本质上是迭代器的语法糖,等价于:
for (auto it = container.begin(); it != container.end(); ++it) {auto element = *it; // 自动解引用
}
以上就是这篇博客的全部内容,下一篇我们将继续探索STL中String里更多精彩内容。
我的个人主页,欢迎来阅读我的其他文章
https://blog.csdn.net/2402_83322742?spm=1011.2415.3001.5343
我的C++知识文章专栏
欢迎来阅读指出不足
https://blog.csdn.net/2402_83322742/category_12880513.html?spm=1001.2014.3001.5482
非常感谢您的阅读,喜欢的话记得三连哦 |