跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 标准库 string 类详解:接口、原理与模拟实现

综述由AI生成C++ 标准库中的 string 类是处理文本的核心工具。本文系统讲解了 string 类的常用接口,包括构造、容量操作、访问遍历及修改方法。重点剖析了底层内存管理机制,对比浅拷贝与深拷贝的区别,并深入探讨了写时拷贝(COW)的工作原理及其在 Linux fork、Redis 等场景的应用。此外,还涵盖了面试中常见的 string 类模拟实现要点,帮助开发者理解资源管理与性能优化。

禅心发布于 2026/3/24更新于 2026/5/33 浏览
C++ 标准库 string 类详解:接口、原理与模拟实现

C++ 标准库 string 类详解

一、为什么学习 string 类?

在 C 语言中,字符串是以 char* 形式存在的字符数组,需要手动管理内存和终止符。而在 C++ 中,std::string 类封装了这些细节,提供了更安全、便捷的字符串处理能力。

掌握 string 类不仅是日常开发的基础,也是面试中的高频考点。常见的面试题包括字符串转换整数、字符串相加等算法题,以及考察底层实现的模拟实现问题。

二、C++ 标准库中的 string 类

在使用 string 类时,必须包含 <string> 头文件并使用 using namespace std;。

2.1)auto 和范围 for

auto 关键字

auto 的核心功能是让编译器通过初始值来推导变量的类型。这意味着使用 auto 时,变量必须初始化。

auto x = 10;      // x 被推导为 int
auto y = 3.14;    // y 被推导为 double
auto ptr = &x;    // ptr 被推导为 int*

用 auto 声明指针类型时,auto 和 auto* 没有区别;但声明引用类型时必须加 &。

int x = 10;
int& ref = x;
auto c = ref;     // c 的类型是 int (引用被丢弃,发生值拷贝)
auto& d = ref;    // d 的类型是 int& (显式声明为引用)
d = 30;           // x 变成了 30
c = 20;           // x 依然是 10,因为 c 是独立变量

注意,在同一行声明多个变量时,它们必须是相同的类型。auto 不能作为函数参数(C++17 以前),建议谨慎用作返回值。

范围 for 循环

C++11 引入了基于范围的 for 循环,语法结构如下:

for(declaration : range) {
    // 循环体
}
  • 第一部分 (declaration): 用于存放当前迭代到的元素。
  • 第二部分 (range): 要遍历的范围(如数组、容器)。

示例:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main() {
    int array[] = {1, 2, 3, 4, 5};
    
    // C++98 的遍历方式
    for(int i = 0; i < sizeof(array)/sizeof(array[0]); ++i) {
        array[i] *= 2;
    }
    
    // C++11 的遍历方式
    for(auto& e : array) e *= 2;  // 使用引用修改原始数据
    for(auto e : array) cout << e << " ";
    
    string str("hello world");
    for(char ch : str) {
        cout << ch << " ";
    }
    cout << endl;
    return 0;
}

范围 for 不仅能作用于数组,也能作用于容器对象(如 std::string)。对于 string,循环会自动调用 begin() 和 end(),无需手动管理迭代器。

2.2)string 类的常用接口

1)构造方法
构造函数功能说明
string()构造空的 string 类对象
string(const char* s)用 C-string 来构造 string 类对象
string(size_t n, char c)构造包含 n 个字符 c 的对象
string(const string& s)拷贝构造函数
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1;                  // 空字符串
    string s2("Hello World");   // 由字符串常量构造
    string s3(10, '*');         // 10 个星号
    string s4(s2);              // 拷贝构造
    return 0;
}
2)容量操作
函数名称功能说明
size() / length()返回字符串有效字符长度
capacity()返回空间总大小
empty()检测字符串是否为空串
clear()清空有效字符
reserve()为字符串预留空间
resize()改变有效字符个数

注意事项:

  1. size() 与 length() 底层原理相同,为了与其他容器接口保持一致。
  2. clear() 只清空有效字符,不改变底层空间大小(capacity)。
  3. resize(n, c) 改变有效字符个数,多出的空间用字符 c 填充。
  4. reserve(res_arg) 只为字符串预留空间,不改变有效元素个数;只有当参数大于当前底层空间时才会扩容。
3)访问及遍历操作
函数名称功能说明
operator[pos]下标访问,可修改
begin() + end()正向迭代器
rbegin() + rend()反向迭代器
范围 forC++11 新遍历方式
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "Hello";
    
    // 下标访问
    cout << "下标为 1 的字符:" << s[1] << endl;
    s[0] = 'h';
    
    // 迭代器遍历
    cout << "正向迭代器遍历:";
    for(string::iterator it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 反向迭代器遍历
    cout << "反向迭代器遍历:";
    for(string::reverse_iterator rit = s.rbegin(); rit != s.rend(); ++rit) {
        cout << *rit << " ";
    }
    cout << endl;
    
    // 范围 for
    cout << "范围 for 遍历:";
    for(char c : s) {
        cout << c << "-";
    }
    cout << endl;
    return 0;
}

Iterator 小拓展: 最常用的四种组合:

  1. iterator:能读能改,正着走。
  2. const_iterator:只能读,正着走。
  3. reverse_iterator:能读能改,倒着走。
  4. const_reverse_iterator:只能读,倒着走。
4)修改操作
函数名称功能说明
push_back尾插单个字符
append追加字符串
operator+=追加字符串或字符
c_str()返回 C 格式字符串
find() + npos查找子串
substr()截取子串
#include <iostream>
using namespace std;

int main() {
    string s = "Hello";
    
    // push_back
    s.push_back('!');
    
    // append
    s.append(" Welcome");
    
    // operator+= (最常用)
    s += " to C++";
    
    // c_str
    const char* cStr = s.c_str();
    cout << "C-Style: " << cStr << endl;
    
    // find
    size_t pos = s.find("Welcome");
    if(pos != string::npos) {
        cout << "Found at: " << pos << endl;
    }
    
    // substr
    string sub = s.substr(7, 7);
    cout << "Sub-string: " << sub << endl;
    return 0;
}

注意事项:

  1. 追加字符有三种方式:push_back(c)、append(n, c)、+=。其中 += 最灵活且常用。
  2. 如果预估要存较多字符,先调用 reserve() 预留空间可以减少频繁扩容带来的性能消耗。
5)非成员函数
函数功能说明
operator+连接字符串(尽量少用,传值返回效率低)
operator>>输入运算符重载
operator<<输出运算符重载
getline()获取一行字符串
关系运算符大小比较
string full_name;
cin >> full_name; // 遇到空格停止
gline(cin, full_name); // 读取整行

string str1 = "apple";
string str2 = "banana";
if(str1 < str2) {
    cout << "apple 排在 banana 前面" << endl;
}
6)VS 和 g++ 下 string 结构的说明

不同编译器对 string 的内部实现可能不同。以 32 位平台为例:

  • VS 下:string 占 28 字节。内部有一个联合体,当字符串长度小于 16 时使用内部固定数组,大于等于 16 时从堆上开辟空间。此外还有保存长度和容量的字段。
  • G++ 下:通常采用写时拷贝(COW)机制。string 对象占 4 个字节,包含一个指针指向堆空间,堆空间中存储空间大小、有效长度、引用计数等信息。

2.3)string 类的模拟实现

在面试中,经常要求模拟实现 string 类,重点在于构造、拷贝构造、赋值运算符重载和析构函数。

1)经典的 string 类问题

初学者容易忽略拷贝构造函数,导致浅拷贝问题。

class String {
public:
    String(const char* str = nullptr) {
        if(nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    ~String() {
        if(_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

void TestString() {
    String s1("hello bit!!!");
    String s2(s1); // 触发隐式拷贝构造
}

问题分析:

  1. s1 创建时在堆上申请内存,_str 指向该地址。
  2. s2 创建时,编译器生成默认拷贝构造函数,执行浅拷贝,将 s1._str 的地址直接赋给 s2._str。
  3. 析构时,s2 先释放内存,随后 s1 再次释放同一块内存,导致程序崩溃(Double Free)。

解决方案: 手动实现深拷贝的拷贝构造函数。

String(const String& s) {
    _str = new char[strlen(s._str) + 1];
    strcpy(_str, s._str);
}
2)浅拷贝 VS 深拷贝
  • 传统写法:在赋值运算符重载中,先检查自赋值,再分配新内存,复制数据,释放旧内存。
  • 现代写法:利用交换法(Copy-and-Swap)。通过传值参数构造临时对象,然后交换资源。这样既利用了移动语义的效率,又保证了异常安全。
class String {
public:
    /*拷贝构造函数*/
    String(const String& s) : _str(nullptr) {
        String strTmp(s._str);
        swap(_str, strTmp._str);
    }
    
    /*赋值运算符重载 (传值方式)*/
    String& operator=(String s) {
        std::swap(_str, s._str);
        return *this;
    }
    
    /*析构函数*/
    ~String() {
        if(_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};
3)写时拷贝(COW)

什么是写时拷贝? COW 结合了浅拷贝和深拷贝的优点。读操作时共享内存,写操作时才真正分配新内存并复制数据。核心思想是延迟执行。

工作机制:

  1. 初始状态:进程 A 拥有内存页 Page 1。
  2. 发生拷贝(如 fork):进程 B 也指向 Page 1,权限设为只读。
  3. 安全读取:A 和 B 共享数据,无开销。
  4. 触发写入:B 尝试修改 Page 1,触发缺页异常。
  5. 内核介入:内核分配新物理内存 Page 2,复制数据,映射给 B,权限恢复为读写。
  6. 继续执行:B 在 Page 2 上修改,A 的数据保持不变。

应用场景:

  1. Linux fork():父子进程初始共享内存,修改时才拷贝,极大提升进程创建速度。
  2. Redis bgsave:主线程 fork 子进程持久化,利用 COW 避免阻塞主线程。
  3. Java CopyOnWriteArrayList:读多写少场景下的线程安全 List,写时复制数组。
  4. 容器技术:Docker 镜像分层存储,启动容器时共享底层镜像层,修改时复制到可写层。

优缺点总结:

  • 优点:节省内存/磁盘资源,提升性能,并发读安全。
  • 缺点:不可预知的延迟(写时触发),写放大开销(如 Java 中修改一个元素需复制整个数组)。

2.4)面试中 string 的一种正确写法

面试实现 String 类时,至少应满足以下要求:

  1. 支持定义变量、赋值、复制。
  2. 能用作函数参数及返回类型。
  3. 能作为标准库容器(如 vector)的元素类型。

2.5)STL 中的 string 类怎么了?

随着 C++ 标准的演进,std::string 的实现也在不断优化。早期版本可能存在性能瓶颈或特定平台的差异,现代编译器通常针对常见场景进行了深度优化(如短字符串优化 SSO)。理解其底层原理有助于我们在实际开发中做出更合适的选择。


结语

std::string 是 C++ 中最常用的工具之一。深入理解其接口、内存管理及实现原理,不仅能写出更高效的代码,也能从容应对各类技术面试。

目录

  1. C++ 标准库 string 类详解
  2. 一、为什么学习 string 类?
  3. 二、C++ 标准库中的 string 类
  4. 2.1)auto 和范围 for
  5. auto 关键字
  6. 范围 for 循环
  7. 2.2)string 类的常用接口
  8. 1)构造方法
  9. 2)容量操作
  10. 3)访问及遍历操作
  11. 4)修改操作
  12. 5)非成员函数
  13. 6)VS 和 g++ 下 string 结构的说明
  14. 2.3)string 类的模拟实现
  15. 1)经典的 string 类问题
  16. 2)浅拷贝 VS 深拷贝
  17. 3)写时拷贝(COW)
  18. 2.4)面试中 string 的一种正确写法
  19. 2.5)STL 中的 string 类怎么了?
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Java 及 Spring 环境下 Redis 操作详解
  • AI 技能(Skills):面向任务自动化的模块化执行范式
  • GLM-4.5 深度技术解析:智谱 AI 全能开源基座模型
  • Whisper.cpp 离线语音识别完整使用指南
  • OpenClaw 汉化中文版部署指南:npm/Docker/脚本三种安装方式及常见问题解决
  • Clawdbot 接入飞书机器人
  • 基于 YOLOv13 的无人机电动自行车违规载人检测系统实战
  • 基于 FPGA 的北斗导航自适应抗干扰算法设计与实现
  • AI 原生重构低代码:开发行业迎来技术变革
  • WSL2 从零部署 OpenClaw AI 助手:安装配置指南
  • VS Code 前端开发必备插件指南:10 款工具与配置详解
  • Unitree SDK2 Python 机器人控制接口使用指南
  • C++ 中 stack 类的实现原理与接口详解
  • 使用 PyTorch 构建 GPT 模型
  • 最长连续序列:哈希集合 O(n) 解法
  • Kimi Code:Moonshot AI 推出的智能编程助手
  • AI 辅助 FPGA 开发与 Vivado 配置实践
  • GitHub Copilot 完整使用指南:从安装到高级技巧
  • Spring Boot 实战:基于 WebClient 的 SSE 流式接口
  • Swin2SR 在 AI 绘图后期的应用:Midjourney 草图 4K 放大实操

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online