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必须包含这个头文件 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::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 std::cout std::cout word += "!"; // 给每个字符串添加感叹号 } // 输出修改后的字符串 for (const auto& word : words) { std::cout // 遍历 vector std::vector1, 2, 3}; for (auto num : nums) { // 自动推导为 int std::cout {"Alice", 90}, {"Bob", 85}}; for (const auto& pair : scores) { // pair 是 std::pair1, 2, 3}; for (auto x : arr) { ... } // 动态数组(通过指针访问时**不适用**,见下文注意事项) int* dyn_arr = new int[3]{1, 2, 3}; // for (auto x : dyn_arr) { ... } // 错误!无法推导数组大小 // 遍历每个字符 std::cout ... } // 需要转为 std::string 1, 3, 5, 7}) { // 直接遍历花括号初始化列表 std::cout 1, 2, 3}; for (auto& num : nums) { // 引用传递,修改原数组 num *= 2; } // 避免拷贝 string ... } auto element = *it; // 自动解引用 }
-
-


