跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

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

C++ string 类解决了 C 语言字符串管理的痛点,提供安全便捷的操作。涵盖 auto 与范围 for 用法,解析构造、容量、访问、修改等常用接口。重点讲解浅拷贝与深拷贝的区别,以及写时拷贝机制在 fork、Redis 等场景的应用。通过对比 VS 与 G++ 下 string 结构差异,深入理解底层内存管理,并提供面试中 string 类的正确实现思路,帮助开发者掌握高效安全的字符串处理方式。

未来可期发布于 2026/3/24更新于 2026/7/529 浏览
C++ 标准库 string 类详解:接口、内存管理与模拟实现

为什么学习 string 类

在 C 语言中,字符串是以字符数组的形式存在的,处理起来往往需要手动管理内存和边界,容易引发缓冲区溢出等安全问题。C++ 引入的 std::string 类封装了这些底层细节,提供了更安全、便捷的字符串操作方式。

C++ 标准库中的 string 类

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

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

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

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

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

示例:

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

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

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

string 类的常用接口

1)string 类的常用构造
构造函数功能说明
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)string 类对象的容量操作
函数名称功能说明
size / length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串是否为空串
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成 n 个

注意事项:

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

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

对于 string 来说,常用的四种组合包括:iterator(能读能改,正着走)、const_iterator(只能读,正着走)、reverse_iterator(能读能改,倒着走)、const_reverse_iterator(只能读,倒着走)。

4)string 类对象的修改操作
函数名称功能说明
push_back在字符串后尾插字符
append在字符串后追加一个字符串
operator+=在字符串后追加字符串
c_str返回 C 格式字符串
find + npos查找子串位置
rfind从右向左找字符
substr截取子串
#include <iostream>
using namespace std;

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

追加字符的三种方式:

  1. push_back(c):只能插入单个字符。
  2. append(n, c):表示追加 n 个字符 c。
  3. +=:最常用,既可以连接单个字符,也可以连接字符串。

使用 reserve 预留空间: 如果预估要存较多字符,先预留空间可以减少频繁扩容带来的性能消耗。

string s;
s.reserve(50);  // 预留 50 字节
5)string 类非成员函数
函数功能说明
operator+尽量少用,传值返回效率低
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators大小比较
string full_name;
getline(cin, full_name);
cout << "你好," << full_name << endl;

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

VS 下 string 的结构 string 总共占 28 个字节。内部有一个联合体,当字符串长度小于 16 时,使用内部固定的字符数组存放;大于等于 16 时,从堆上开辟空间。此外还有保存长度的字段和保存容量的字段。

G++ 下 string 的结构 G++ 下,string 是通过写时拷贝实现的,对象总共占 4 个字节,内部只包含了一个指针,指向堆空间。堆空间中包含了空间总大小、字符串有效长度、引用计数等信息。

string 类的模拟实现

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

浅拷贝问题 初学者容易忽略拷贝构造函数,导致浅拷贝。默认拷贝构造函数只是简单复制指针地址,析构时会重复释放同一块内存,导致程序崩溃。

class String {
public:
    String(const char* str = nullptr) {
        if (str == nullptr) {
            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);  // 触发隐式拷贝构造,若未自定义则为浅拷贝
}

解决方案 手动实现拷贝构造函数,为新对象申请独立内存空间。

String(const String& s) {
    _str = new char[strlen(s._str) + 1];
    strcpy(_str, s._str);
}

传统 vs 现代的 string 类

传统写法 直接分配内存并拷贝,赋值运算符需检查自赋值并释放旧资源。

现代写法 利用移动语义或交换法优化。例如赋值运算符采用传值语义,形参是局部副本,函数结束时自动释放旧资源,只需交换指针即可。

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

    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;
};

写时拷贝(COW)

什么是写时拷贝? 写时拷贝是一种结合了浅拷贝和深拷贝优点的策略。读操作时共享内存,写操作时才真正分配新内存进行拷贝。核心思想是延迟执行。

工作机制 以操作系统内存页为例:进程 A 拥有 Page 1。发生拷贝(如 fork)时,系统不复制数据,而是让 A 和 B 都指向 Page 1,并将权限设为只读。当 B 试图写入时触发缺页异常,内核分配新页面 Page 2 并复制数据,B 映射到 Page 2,A 保持 Page 1。

应用场景

  1. Linux fork():父子进程初始共享内存,仅修改时拷贝,提升创建速度。
  2. Redis bgsave:子进程负责持久化,父进程继续处理请求,避免阻塞。
  3. Java CopyOnWriteArrayList:读多写少场景,写时复制数组。
  4. Docker/ZFS:镜像分层存储,容器层可写,底层镜像只读。

优缺点总结

  • 优点:节省资源,提升性能,并发读安全。
  • 缺点:不可预知的延迟,写放大开销。

面试中 string 的正确写法

C++ 面试题通常要求实现一个 String 类,至少满足:

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

实现时需确保资源管理正确,遵循三法则(Rule of Three)或五法则(Rule of Five),处理好深拷贝与移动语义。

目录

  1. 为什么学习 string 类
  2. C++ 标准库中的 string 类
  3. auto 和范围 for
  4. string 类的常用接口
  5. 1)string 类的常用构造
  6. 2)string 类对象的容量操作
  7. 3)string 类对象的访问及遍历操作
  8. 4)string 类对象的修改操作
  9. 5)string 类非成员函数
  10. 6)VS 和 G++ 下 string 结构的说明
  11. string 类的模拟实现
  12. 写时拷贝(COW)
  13. 面试中 string 的正确写法
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Stable Diffusion 3.5 硬件准备与环境配置:低显存优化方案
  • 自动化维修流水线:多任务并行调度最短时间求解
  • JavaScript 零基础入门核心知识点
  • AI 驱动的在线考试系统设计与实现
  • OpenVLA 深度解析:基于 Prismatic VLM 的离散化动作预测方案
  • std 与 STL 辨析:C++ 标准库核心概念详解
  • Python 数据分析入门:集中趋势与离散程度详解
  • CCF-CV 企业交流会直播回顾:AI 安全治理与可信未来
  • 网络安全入门指南:学习路线与方向选择
  • 工程级前端智能体 FrontAgent
  • JSP 基础:深入理解前后端交互与核心对象
  • Android 大厂面试真题解析与复习思路指南
  • Python 为何成为 AI 开发主流语言?对比 Java 与 Ruby
  • C 语言多线程与并发编程:提高程序执行效率
  • Spring Cloud Config 与 Apollo 架构深度解析
  • 混合专家模型 (MoE) 详解
  • 人工智能基础概念与核心原理解析
  • Flutter BIP340 适配鸿蒙 HarmonyOS 实战:Schnorr 签名应用
  • 前缀和专题:和为 k 的子数组与和可被 k 整除的子数组
  • AI 大模型通信机制解析:流式传输与数据封装逻辑

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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