C++从入门到实战(十五)String(上)介绍STL与String的关系,为什么有string类,String有什么用

C++从入门到实战(十五)String(上)

  • 前言
  • 一、STL与String的关系
    • [1. STL 是什么?](#1. STL 是什么?)
    • [2. String 是什么?](#2. String 是什么?)
    • [3. String 与 STL 的关系](#3. String 与 STL 的关系)
  • 二、为什么有string类,有什么用
    • [1. 为什么需要 string 类?](#1. 为什么需要 string 类?)
    • [2. string 类的核心作用:](#2. string 类的核心作用:)
    • [3. string 类的其他能力:(后面会详细讲,现在看一下就好)](#3. string 类的其他能力:(后面会详细讲,现在看一下就好))
    • [4. 和 C风格字符串对比](#4. 和 C风格字符串对比)
  • 三、auto和范围for
    • [1. auto 关键字:](#1. auto 关键字:)
      • [1.1 基本用法](#1.1 基本用法)
      • [1.2 细节注意](#1.2 细节注意)
      • [1.3 与Python的对比](#1.3 与Python的对比)
    • [2. 范围for循环:](#2. 范围for循环:)
      • [2.1 基本语法](#2.1 基本语法)
      • [2.2 修改元素:用引用](#2.2 修改元素:用引用)
    • [3. 适用范围](#3. 适用范围)
      • (1)适用场景
        • [1. STL容器(如 vector、list、map 等)](#1. STL容器(如 vector、list、map 等))
        • [2. 普通数组(静态/动态)](#2. 普通数组(静态/动态))
        • [3. 字符串(`std::string` 或 C风格字符串)](#3. 字符串(std::string 或 C风格字符串))
        • [4. 初始化列表(临时构造的集合)](#4. 初始化列表(临时构造的集合))
      • (2)不适用场景(了解就好)
        • [1. 需要索引时](#1. 需要索引时)
        • [2. 动态修改容器大小](#2. 动态修改容器大小)
        • [3. 通过指针访问的数组](#3. 通过指针访问的数组)
        • [4. 不支持迭代器的自定义类型](#4. 不支持迭代器的自定义类型)
      • (3)关键细节
        • [1. 修改元素:用引用 &](#1. 修改元素:用引用 &)
        • [2. 避免拷贝:用 const auto&](#2. 避免拷贝:用 const auto&)
        • [3. 底层原理](#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包含多种容器(如vectorlistmap),而string是其中专门处理文本的容器。
  • 共享 STL 的特性
    • 动态管理内存 :无需手动分配/释放内存,如string s = "hello";
    • 支持迭代器 :可以像遍历数组一样遍历字符串(如s.begin()s.end())。
    • 丰富的方法 :如s.length()s.substr()s.find()等。

二、为什么有string类,有什么用

1. 为什么需要 string 类?

生活场景

假设你是老师,要记录学生的姓名。如果没有 string,你得用 C风格字符数组(像用便签纸记录):

cpp 复制代码
char name[100];  // 必须提前指定最大长度(比如最多100个字符)

但现实中,名字长度不一(有人叫"张三",有人叫"阿卜杜勒·拉赫曼"),用固定长度的数组会遇到两个问题:

  • 问题1 :长度不够(比如名字超过100个字符),数组会越界(像便签纸写不下,字会挤到外面)。
  • 问题2:长度浪费(比如名字只有3个字符,却占了100个位置)。

string 类就像一个自动伸缩的便签纸,你写多少字,它就自动扩展多长,完美解决上述问题!

2. string 类的核心作用:

cpp 复制代码
#include <string>  // 使用string必须包含这个头文件

std::string name = "阿卜杜勒·拉赫曼";  // 无需指定长度,自动适应内容
  • 优势1:无需关心内存大小,随便存多长的字符串。
  • 优势2:无需手动释放内存,用起来更安全(不会忘记释放导致内存泄漏)。

3. string 类的其他能力:(后面会详细讲,现在看一下就好)

生活场景

假设你要给学生分组,需要把姓和名拼起来(比如"张" + "三" → "张三"),或者判断名字是否包含"小"字。

string 类,这些操作就像玩积木一样简单:

cpp 复制代码
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 基本用法

cpp 复制代码
auto num = 42;       // 编译器猜:int
auto pi = 3.14;      // 编译器猜:double
auto name = "Alice"; // 编译器猜:const char*

优势

  • 代码更简洁,不用写冗长的类型名(比如 std::vector<int>::iterator)。
  • 避免手动写错类型。

1.2 细节注意

  • 指针与引用

    cpp 复制代码
    int x = 10;
    auto ptr = &x;     // auto 推导为 int*(auto* 也可以,但没必要)
    auto& ref = x;     // 必须加 &,否则 ref 会是 int(复制值)
  • 同一行声明多个变量

    cpp 复制代码
    auto a = 1, b = 2;     // 正确:都是 int
    auto c = 3, d = 3.14;  // 错误:c 是 int,d 是 double
  • 不能做函数参数

    cpp 复制代码
    void func(auto x) { }  // 错误:编译器无法推导参数类型

1.3 与Python的对比

特性 C++ auto Python 变量
类型推导时机 编译时(静态类型) 运行时(动态类型)
类型是否固定 一旦推导,类型固定 运行中可以改变
示例 auto x = 1; x = "hello"; // 错误 x = 1; x = "hello"; // 正确

2. 范围for循环:

生活场景

假设你要挨个检查教室里的桌子,传统写法是:

  1. 从第一张桌子开始(初始化)。
  2. 检查当前桌子,然后走到下一张(迭代)。
  3. 直到走完所有桌子(终止条件)。

而范围for循环就像:"你直接说'检查所有桌子',系统自动帮你挨个检查,不用操心细节"。

2.1 基本语法

cpp 复制代码
 // 字符串数组
    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 修改元素:用引用

cpp 复制代码
// 使用引用(&)遍历并修改每个字符串
    for (auto& word : words) 
    {
        word += "!";  // 给每个字符串添加感叹号
    }
    
    // 输出修改后的字符串
    for (const auto& word : words) 
    {
        std::cout << word << " ";
    }

3. 适用范围

(1)适用场景

1. STL容器(如 vector、list、map 等)
cpp 复制代码
#include <vector>
#include <map>
#include <iostream>

int main() {
    // 遍历 vector
    std::vector<int> nums = {1, 2, 3};
    for (auto num : nums) {  // 自动推导为 int
        std::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. 普通数组(静态/动态)
cpp 复制代码
// 静态数组
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风格字符串)
cpp 复制代码
#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. 初始化列表(临时构造的集合)
cpp 复制代码
for (int x : {1, 3, 5, 7}) {  // 直接遍历花括号初始化列表
    std::cout << x << " ";
}

(2)不适用场景(了解就好)

1. 需要索引时

范围for循环隐藏了迭代器/索引,无法直接获取当前元素的位置。
替代方案 :用传统for循环或 std::views::enumerate(C++20)。

2. 动态修改容器大小

遍历时添加/删除元素可能导致迭代器失效(如 vector 扩容)。
替代方案:用传统for循环,手动控制迭代器。

3. 通过指针访问的数组
cpp 复制代码
int* ptr = arr;  // arr 是普通数组
// for (auto x : ptr) { ... }  // 错误!无法推导数组大小
4. 不支持迭代器的自定义类型

范围for依赖 begin()end() 方法,若类型未实现则无法使用。

(3)关键细节

1. 修改元素:用引用 &
cpp 复制代码
std::vector<int> nums = {1, 2, 3};
for (auto& num : nums) {  // 引用传递,修改原数组
    num *= 2;
}
2. 避免拷贝:用 const auto&

遍历大对象(如 vector<string>)时,值传递会拷贝对象,用引用更高效:

cpp 复制代码
for (const auto& str : strs) {  // 避免拷贝 string
    ...
}
3. 底层原理

范围for循环本质上是迭代器的语法糖,等价于:

cpp 复制代码
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

|--------------------|
| 非常感谢您的阅读,喜欢的话记得三连哦 |

相关推荐
ClearViper333 分钟前
Java的多线程笔记
java·开发语言·笔记
敷啊敷衍33 分钟前
深入探索 C++ 中的 string 类:从基础到实践
开发语言·数据结构·c++
学地理的小胖砸43 分钟前
【Python 面向对象】
开发语言·python
什么名字都被用了1 小时前
编译openssl源码
c++·openssl
神经毒素1 小时前
WEB安全--Java安全--LazyMap_CC1利用链
java·开发语言·网络·安全·web安全
ai.Neo2 小时前
牛客网NC22157:牛牛学数列2
数据结构·c++·算法
酷炫码神2 小时前
C#语法基础
开发语言·c#
ddd...e_bug2 小时前
GMT之Bash语言使用
开发语言·bash
码农秋2 小时前
填坑记: 古董项目Apache POI 依赖异常排除
开发语言·tomcat·jsp·poi·依赖冲突
qq_653644462 小时前
如何查看打开的 git bash 窗口是否是管理员权限打开
开发语言·windows·git·bash