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

C++ 标准库 string 类详解与模拟实现

C++ string 类详解涵盖构造、常用接口、底层结构及模拟实现。对比 C 风格字符串,阐述 OOP 优势。解析 auto 关键字与范围 for 循环在 string 遍历中的应用。区分 VS 与 g++ 下 string 内存布局差异。重点讲解浅拷贝与深拷贝原理,通过传统与现代写法演示规则三的实现,辅助理解资源管理。

ServerBase发布于 2026/3/29更新于 2026/10/2186 浏览
C++ 标准库 string 类详解与模拟实现

C++ 标准库 string 类详解与模拟实现

为什么学习 string 类

在 C 语言中,字符串是以 \0 结尾的字符集合。虽然标准库提供了一些 str 系列函数,但这些函数与字符串本身是分离的,不太符合面向对象(OOP)的思想。更重要的是,底层空间需要用户自己管理,容易出现越界访问等安全隐患。

C++ 引入 string 类正是为了解决这些问题,它封装了内存管理、大小计算等操作,让开发者能更安全、高效地处理文本。

基础语法与工具

auto 关键字与范围 for

auto 关键字 在 C++11 之前,auto 表示自动存储期,意义不大。C++11 重新定义了它:auto 不再是一个存储类型指示符,而是作为类型推导指示符。编译器会在编译期根据初始化表达式推导变量类型。

  • 指针与引用:用 auto 声明指针时,auto 和 auto* 没有区别;但声明引用时必须加 &。
  • 多变量声明:同一行声明多个变量时,它们必须是相同的类型,否则编译器会报错。
  • 限制:auto 不能作为函数参数,可以做返回值但需谨慎使用;不能直接用来声明数组。
#include <iostream>
#include <typeinfo>
using namespace std;

int fun1() {
    return 1;
}

void func2(auto a) {} // 编译报错,auto 不能做参数

auto func3() {
    return 3; // 可以返回 auto,但建议谨慎使用
}

int main() {
    int a = 1;
    auto b = a;
    auto c = ;
     d = ();
    
    
    
    cout << (b).() << endl;
    cout << (c).() << endl;
    cout << (d).() << endl;
    
     x = ;
     y = &x;
     *z = &x;
     &m = x;
    
    
    
     ;
}
'a'
auto
fun1
// auto e; // 编译报错,必须具有初始值设定项
typeid
name
typeid
name
typeid
name
int
1
auto
auto
auto
// auto cc = 3, dd = 4.0; // 编译报错,类型不一致
return
0

范围 for 循环 C++11 引入了基于范围的 for 循环,简化了集合遍历。括号由冒号分为两部分:迭代变量和被迭代范围。它会自动处理迭代器,无需手动判断结束条件。

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

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    
    // C++98 写法
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        arr[i]++;
        cout << arr[i] << " ";
    }
    cout << endl;
    
    // C++11 写法
    for (auto &e : arr) { // 引用修改原数组
        e++;
        cout << e << " ";
    }
    cout << endl;
    
    string str("hello world");
    for (auto c : str) {
        cout << c << " ";
    }
    cout << endl;
    
    return 0;
}

string 类常用接口

1. 构造方式

函数名称功能说明
string()构造空的 string 对象
string(const char* s)用 C 风格字符串构造
string(size_t n, char c)包含 n 个字符 c
string(const string& s)拷贝构造
void Test() {
    string s1;              // 空字符串
    string s2("hello world"); // C 字符串构造
    string s3(s2);          // 拷贝构造
}

2. 容量操作

函数名称功能说明
size() / length()返回有效字符长度
capacity()返回总空间大小
empty()检测是否为空串
clear()清空有效字符,不释放空间
reserve()预留空间,不改变有效元素个数
resize()调整有效字符个数,可指定填充字符

注意:

  • size() 与 length() 原理相同,为了与其他容器接口一致,通常用 size()。
  • clear() 只清空内容,底层空间不变。
  • resize(n) 扩容时用 \0 填充,resize(n, c) 用字符 c 填充。
  • reserve() 若参数小于当前容量,则不改变容量。

3. 访问及遍历

函数名称功能说明
operator[]返回指定位置字符
begin() + end()获取首尾迭代器
rbegin() + rend()获取反向迭代器
范围 forC++11 简洁遍历方式

4. 修改操作

函数名称功能说明
push_back()尾部插入字符
append()追加字符串
operator+=追加字符串或字符
c_str()返回 C 风格字符串指针
find() / rfind()查找字符/子串位置
substr()截取子串

提示:

  • 尾部追加字符时,s.push_back(c)、s.append(1, c) 和 s += 'c' 效果类似,+= 最常用。
  • 若能预估字符数量,先用 reserve() 预留空间可减少内存重分配开销。

5. 非成员函数

函数名称功能说明
operator+连接字符串(传值效率低,尽量少用)
operator>>输入运算符重载
operator<<输出运算符重载
getline()获取一行字符串
关系运算符大小比较

底层结构差异

不同编译器下 string 的内部实现有所不同,以 32 位平台为例:

VS 下的结构

VS 的 string 占用 28 字节。采用短字符串优化(SSO):

  • 当字符串长度小于 16 时,使用内部固定字符数组存放,无需堆分配。
  • 当长度大于等于 16 时,从堆上开辟空间。
  • 结构体包含联合体、长度字段、容量字段及其他指针信息。

g++ 下的结构

g++ 的 string 通常采用写时拷贝(Copy-On-Write),对象本身仅占 4 字节(一个指针)。

  • 指针指向堆上的控制块,包含:空间总大小、有效长度、引用计数。
  • 这种设计在多线程环境下需注意线程安全问题。

string 类的模拟实现

面试中常要求手写 string 类,核心在于掌握资源管理规则(Rule of Three/Five)。

浅拷贝问题

如果未显式定义拷贝构造函数和赋值运算符,编译器会生成默认的浅拷贝。这会导致两个对象共享同一块堆内存,析构时发生重复释放,引发程序崩溃。

class String {
public:
    String(const char* str = "") {
        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;
};
// 测试代码中若进行 String s2(s1),将导致浅拷贝错误

深拷贝解决方案

每个对象应拥有独立的资源副本。

传统写法

显式实现拷贝构造函数、赋值运算符重载和析构函数。

class String {
public:
    String(const char* str = "") {
        if (nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    
    // 拷贝构造
    String(const String& s) : _str(new char[strlen(s._str) + 1]) {
        strcpy(_str, s._str);
    }
    
    // 赋值运算符重载
    String& operator=(const String& s) {
        if (this != &s) {
            char* pstr = new char[strlen(s._str) + 1];
            strcpy(pstr, s._str);
            delete[] _str;
            _str = pstr;
        }
        return *this;
    }
    
    ~String() {
        if (_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};
现代写法(异常安全)

利用临时对象和 swap 提高效率,减少内存泄漏风险。

class String {
public:
    String(const char* str = "") {
        if (nullptr == str) {
            assert(false);
            return;
        }
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    
    // 拷贝构造
    String(const String& s) : _str(new char[strlen(s._str) + 1]) {
        strcpy(_str, s._str);
    }
    
    // 赋值运算符重载 (右值引用版本)
    String& operator=(String& s) {
        swap(_str, s._str);
        return *this;
    }
    
    // 赋值运算符重载 (常量引用版本)
    String& operator=(const String& s) {
        if (this != &s) {
            String temp(s);
            swap(_str, temp._str);
        }
        return *this;
    }
    
    ~String() {
        if (_str) {
            delete[] _str;
            _str = nullptr;
        }
    }
private:
    char* _str;
};

扩展阅读与练习

  • 写时拷贝原理
  • STL string 相关问题
  • LeetCode 练习题:反转字母
  • LeetCode 练习题:第一个唯一字符
  • 牛客网练习题:字符串最后一个单词的长度
  • LeetCode 练习题:验证回文串
  • LeetCode 练习题:字符串相加

目录

  1. C++ 标准库 string 类详解与模拟实现
  2. 为什么学习 string 类
  3. 基础语法与工具
  4. auto 关键字与范围 for
  5. string 类常用接口
  6. 1. 构造方式
  7. 2. 容量操作
  8. 3. 访问及遍历
  9. 4. 修改操作
  10. 5. 非成员函数
  11. 底层结构差异
  12. VS 下的结构
  13. g++ 下的结构
  14. string 类的模拟实现
  15. 浅拷贝问题
  16. 深拷贝解决方案
  17. 传统写法
  18. 现代写法(异常安全)
  19. 扩展阅读与练习

更多推荐文章

查看全部
  • Fanuc 机器人与 PLC 基于 Ethernet/IP 的高速通信实战
  • C#属性与C++成员变量的核心哲学差异对比
  • 选择排序详解:直接、树形与堆排序实战
  • Python 安装与环境配置完整指南
  • OpenClaw 登顶 GitHub 星标榜首,单人开发重塑 AI 代理格局
  • C++ 模板机制与 string 类详解
  • 基于 YOLO 标注格式的无人机航拍人员搜救检测数据集
  • WebGIS 实战:WKT 转 GeoJSON 方案及 Leaflet 集成指南
  • Kafka 核心架构与分布式存储详解
  • 鸿蒙金融理财全栈项目:生态合作、用户运营与数据变现
  • OpenClaw 与企业即时通讯平台集成架构研究
  • Android 离线语音识别实践:基于 Whisper 与 TensorFlow Lite 实现本地转录
  • Win11 本地部署 OpenClaw:集成 Telegram 机器人与网页搜索功能
  • 云边端一体化解析:概念、原理与AI基础设施实践
  • AI 四大核心技术详解:LLM、Agent、RAG 与 Skill
  • 基于 LangChain 实现的知识库问答工具
  • Windows 本地部署 Ollama 与 OpenClaw,构建 AI 生产力系统
  • 链表在内存管理与缓存淘汰中的应用
  • 基于 DeepSeek V3.2 构建企业级 Text-to-SQL 专家系统
  • Java 图论基础:有向图与无向图详解

相关免费在线工具

  • 加密/解密文本

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