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

C++ string 类详解与模拟实现

全面解析 C++ string 类,对比 C 语言字符串的局限性,介绍 string 类的基础用法、常用接口(构造函数、容量操作、遍历、修改、查找)、不同编译器的实现差异(VS 小字符串优化、g++ 写时拷贝)。同时涵盖 C++11 新特性 auto 与范围 for 的应用,提供 string 类的模拟实现(浅拷贝、深拷贝、写时拷贝),并通过 LeetCode 风格示例展示实战技巧,最后给出性能优化建议和最佳实践。

ByteFlow发布于 2026/3/30更新于 2026/5/2528 浏览
C++ string 类详解与模拟实现

C++ string 类全面解析

1. 为什么学习 string 类?

1.1 C 语言中的字符串局限性

在 C 语言中,字符串是以 \0 结尾的字符数组,这种表示方式存在几个明显的缺陷:

C 语言字符串的主要问题:

  • 安全性问题:容易发生缓冲区溢出,导致程序崩溃或安全漏洞
  • 内存管理复杂:需要手动管理内存分配和释放,容易造成内存泄漏
  • 功能有限:标准库函数功能相对基础,复杂的字符串操作需要自行实现
  • 不符合面向对象思想:数据与操作分离,不符合现代编程范式
// C 语言字符串操作的典型问题
char str[10];
strcpy(str, "这个字符串太长了会导致溢出"); // 潜在的安全风险
1.2 实际应用需求

在现代编程中,字符串处理占据了极大的比重。无论是 Web 开发、数据处理还是系统编程,都离不开高效的字符串操作。string 类的出现正是为了解决 C 语言字符串的种种痛点。

常见应用场景:

  • 字符串转整型数字
  • 大数相加(字符串形式)

实践建议:在 OJ 题目和实际开发中,string 类已成为字符串处理的首选工具,相比 C 字符串库函数更加安全高效。

2. 标准库中的 string 类

2.1 string 类基础

string 类是 C++ 标准库中用于表示和操作字符串的类,封装了字符串的存储和常见操作。

基本用法:

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

int main() {
    string s1;             // 空字符串
    string s2 = "Hello";   // 直接初始化
    string s3("World");    // 构造函数初始化
    return 0;
}
2.2 C++11 新特性:auto 和范围 for
auto 关键字详解

auto 是 C++11 引入的类型推断关键字,让编译器自动推导变量类型。

auto 的使用规则:

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

int main() {
    // 基本类型推断
    auto a = 10;       // int
    auto b = 3.14;     // double
    auto c = 'A';      // char

    // 指针和引用
    int x = 100;
    auto y = &x;       // int*
    auto* z = &x;      // int* (与上面等价)
    auto& ref = x;     // int&

    // 容器迭代器简化
    map<string, string> dict = {{"apple", "苹果"}, {"banana", "香蕉"}};
    // 传统写法(冗长)
    // map<string, string>::iterator it1 = dict.begin();
    // auto 写法(简洁)
    auto it2 = dict.begin();

    // 遍历 map
    for (auto it = dict.begin(); it != dict.end(); ++it) {
        cout << it->first << ": " << it->second << endl;
    }
    return 0;
}

auto 的限制:

  • 必须初始化:auto x; // 错误
  • 多变量声明必须类型一致:auto a=1, b=2.0; // 错误
  • 不能用于函数参数(但可以用于返回值)
  • 不能声明数组:auto arr[] = {1,2,3}; // 错误
范围 for 循环(Range-based for loop)

范围 for 提供了更简洁的遍历语法,特别适合容器遍历。

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

int main() {
    // 数组遍历
    int arr[] = {1, 2, 3, 4, 5};
    // 传统遍历方式
    for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 范围 for 遍历(只读)
    for (auto elem : arr) {
        cout << elem << " ";
    }
    cout << endl;

    // 范围 for 遍历(可修改)
    for (auto& elem : arr) {
        elem *= 2; // 修改元素
    }

    // 字符串遍历
    string str = "Hello";
    for (auto ch : str) {
        cout << ch << " ";
    }
    cout << endl;
    return 0;
}

范围 for 的底层原理:编译器会将范围 for 转换为基于迭代器的普通循环。

2.3 string 类常用接口详解
2.3.1 构造函数
构造函数功能说明示例
string()创建空字符串string s1;
string(const char* s)用 C 字符串构造string s2("hello");
string(size_t n, char c)n 个字符 c 组成的字符串string s3(5, 'A'); // "AAAAA"
string(const string& str)拷贝构造string s4(s2);
void testConstructors() {
    string s1;             // 空字符串
    string s2("Hello World");// 从 C 字符串构造
    string s3(s2);         // 拷贝构造
    string s4(10, '*');    // "**********"
    string s5 = "直接赋值"; // 赋值初始化
    cout << "s2: " << s2 << endl;
    cout << "s4: " << s4 << endl;
}
2.3.2 容量操作

重要容量方法:

方法功能说明
size()/length()返回字符串长度两者功能相同,推荐 size()
capacity()返回分配的内存大小通常 ≥ size()
empty()判断是否为空空返回 true,否则 false
clear()清空内容不释放内存,size=0
reserve(size_t n)预留空间避免频繁重新分配
resize(size_t n, char c)调整大小多出部分用 c 填充
void testCapacity() {
    string str = "Hello";
    cout << "长度:" << str.size() << endl;          // 5
    cout << "容量:" << str.capacity() << endl;      // 15(编译器相关)
    cout << "是否为空:" << str.empty() << endl;     // 0(false)
    str.resize(10, '!');
    cout << "调整后:" << str << endl;               // "Hello!!!!!"
    str.reserve(100);
    cout << "预留后容量:" << str.capacity() << endl;
    str.clear();
    cout << "清空后长度:" << str.size() << endl;    // 0
}

resize() 详解:

string s = "Hello";
s.resize(3);           // "Hel"(截断)
s.resize(8, '!');      // "Hel!!!!!"(扩展并填充)
s.resize(10);          // "Hel!!!!! "(扩展,默认填充空格)
2.3.3 元素访问和遍历

多种遍历方式:

void testTraversal() {
    string str = "ABCDE";
    // 1. 下标操作符 []
    for (size_t i = 0; i < str.size(); ++i) {
        cout << str[i] << " "; // A B C D E
    }
    cout << endl;

    // 2. 迭代器
    for (auto it = str.begin(); it != str.end(); ++it) {
        cout << *it << " "; // A B C D E
    }
    cout << endl;

    // 3. 反向迭代器
    for (auto rit = str.rbegin(); rit != str.rend(); ++rit) {
        cout << *rit << " "; // E D C B A
    }
    cout << endl;

    // 4. 范围 for(推荐)
    for (auto ch : str) {
        cout << ch << " "; // A B C D E
    }
    cout << endl;
}
2.3.4 修改操作

常用修改方法:

方法功能示例
push_back(char c)尾部添加字符str.push_back('!')
append(const string& str)追加字符串str.append(" World")
operator+=追加(最常用)str += "!!"
insert(size_t pos, const string& str)插入字符串str.insert(5, "插入")
erase(size_t pos, size_t len)删除子串str.erase(5, 2)
replace(size_t pos, size_t len, const string& str)替换子串str.replace(0, 5, "Hi")
void testModification() {
    string str = "Hello";
    // 追加操作
    str.push_back('!');            // "Hello!"
    str.append(" World");          // "Hello! World"
    str += "!!";                   // "Hello! World!!"(最常用)

    // 插入和删除
    str.insert(6, "C++ ");         // "Hello! C++ World!!"
    str.erase(0, 7);               // "C++ World!!"
    str.replace(4, 5, "String");   // "C++ String!!"
    cout << "最终结果:" << str << endl;
}
2.3.5 字符串操作

查找和子串操作:

void testStringOperations() {
    string str = "Hello World, Hello C++";
    // 查找操作
    size_t pos1 = str.find("Hello");       // 0
    size_t pos2 = str.find("Hello", 1);    // 13(从位置 1 开始找)
    size_t pos3 = str.rfind("Hello");      // 13(从后往前找)

    // 子串提取
    string sub1 = str.substr(6, 5);        // "World"
    string sub2 = str.substr(6);           // "World, Hello C++"

    // 比较操作
    string s1 = "apple", s2 = "banana";
    int result = s1.compare(s2);           // 负数(apple < banana)

    cout << "find 结果:" << pos1 << ", " << pos2 << ", " << pos3 << endl;
    cout << "子串:" << sub1 << ", " << sub2 << endl;
}
2.4 不同编译器下的 string 实现
VS 下的 string 实现(小字符串优化)

Visual Studio 采用小字符串优化 (SSO) 策略:

内存布局(32 位平台):

  • 16 字节缓冲区:长度<16 时使用栈空间
  • 4 字节:字符串长度
  • 4 字节:总容量
  • 4 字节:其他信息
  • 总计 28 字节

优势:短字符串无需堆分配,提高性能。

// VS 中小字符串优化的效果
string shortStr = "short";        // 使用内部缓冲区(栈)
string longStr = "这是一个很长的字符串..."; // 使用堆分配
g++ 下的 string 实现(写时拷贝)

g++ 采用写时拷贝 (Copy-On-Write) 技术:

内存布局:

  • 4 字节指针:指向堆上的结构体
  • 结构体包含:长度、容量、引用计数、字符串数据

优势:拷贝时不立即复制数据,提高拷贝效率。

// g++ 中的写时拷贝
string s1 = "hello";
string s2 = s1;                    // 不复制数据,只增加引用计数
s2[0] = 'H';                       // 此时才真正复制数据(写时拷贝)
2.5 实战练习
示例 1:仅反转字母
class Solution {
public:
    bool isLetter(char ch) {
        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
    }
    string reverseOnlyLetters(string s) {
        if (s.empty()) return s;
        int left = 0, right = s.size() - 1;
        while (left < right) {
            // 找到左边的字母
            while (left < right && !isLetter(s[left])) ++left;
            // 找到右边的字母
            while (left < right && !isLetter(s[right])) --right;
            // 交换字母
            if (left < right) {
                swap(s[left], s[right]);
                ++left;
                --right;
            }
        }
        return s;
    }
};
示例 2:字符串相加(大数加法)
class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.size() - 1, j = num2.size() - 1;
        int carry = 0;
        string result;
        while (i >= 0 || j >= 0 || carry > 0) {
            int digit1 = (i >= 0) ? num1[i--] - '0' : 0;
            int digit2 = (j >= 0) ? num2[j--] - '0' : 0;
            int sum = digit1 + digit2 + carry;
            carry = sum / 10;
            result.push_back('0' + (sum % 10));
        }
        reverse(result.begin(), result.end());
        return result;
    }
};
示例三:字符串最后一个单词的长度
#include <iostream>
using namespace std;

int main() {
    string s;
    getline(cin, s); // 这里不能用流提取。流提取识别不了空格
    size_t pos = s.rfind(' ');
    cout << s.size() - (pos + 1) << endl;
}

getlin 默认三个参数,输入两个时,默认最后一个是空

3. string 类的模拟实现

3.1 浅拷贝问题

浅拷贝的危险性:

// 有问题的 String 类实现
class String {
public:
    String(const char* str = "") {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    ~String() {
        delete[] _str;
    }
private:
    char* _str;
};

void testProblem() {
    String s1("hello");
    String s2(s1); // 浅拷贝:s1 和 s2 指向同一内存
    // 析构时:同一内存被删除两次 → 程序崩溃
}
3.2 深拷贝实现
传统版 String 类
class String {
public:
    // 构造函数
    String(const char* str = "") {
        if (str == nullptr) str = "";
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }

    // 拷贝构造函数(深拷贝)
    String(const String& other) {
        _str = new char[strlen(other._str) + 1];
        strcpy(_str, other._str);
    }

    // 赋值运算符重载
    String& operator=(const String& other) {
        if (this != &other) {
            // 防止自赋值
            char* temp = new char[strlen(other._str) + 1];
            strcpy(temp, other._str);
            delete[] _str; // 释放原有资源
            _str = temp;
        }
        return *this;
    }

    // 析构函数
    ~String() {
        delete[] _str;
    }
private:
    char* _str;
};
现代版 String 类(更优雅)
class String {
public:
    String(const char* str = "") {
        if (str == nullptr) str = "";
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }

    // 拷贝构造函数:利用临时对象交换
    String(const String& other) : _str(nullptr) {
        String temp(other._str); // 用 C 字符串构造临时对象
        swap(_str, temp._str);   // 交换资源
    }

    // 赋值运算符:参数为值传递,利用交换技术
    String& operator=(String other) {
        // 注意:参数为值传递
        swap(_str, other._str);  // 交换资源
        return *this;            // other 析构时会释放原有资源
    }

    ~String() {
        delete[] _str;
    }
private:
    char* _str;
};
3.3 写时拷贝(Copy-On-Write)

写时拷贝是一种优化技术,在读取时共享数据,在修改时才进行实际拷贝。

基本原理:

  1. 多个对象共享同一数据
  2. 引用计数跟踪共享者数量
  3. 当有修改操作时,才进行实际拷贝
// 简化的写时拷贝实现
class CowString {
private:
    struct StringData {
        char* data;
        int refCount; // 引用计数
        StringData(const char* str) {
            data = new char[strlen(str) + 1];
            strcpy(data, str);
            refCount = 1;
        }
        ~StringData() {
            delete[] data;
        }
    };
    StringData* _data;
public:
    // 实现细节略...
};

4. 最佳实践和性能建议

4.1 字符串操作优化
// 不推荐的写法(性能差)
string result;
for (int i = 0; i < 1000; ++i) {
    result += "data"; // 可能多次重新分配内存
}

// 推荐的写法
string result;
result.reserve(5000); // 预先分配足够空间
for (int i = 0; i < 1000; ++i) {
    result += "data"; // 无重新分配
}
4.2 选择合适的方法
string str = "hello";
// 尾部添加字符的三种方式
str.push_back('!'); // 方式 1
str.append(1, '!'); // 方式 2
str += '!';         // 方式 3(最常用)

// 查找操作选择
size_t pos1 = str.find('e');    // 查找字符
size_t pos2 = str.find("ll");   // 查找子串
size_t pos3 = str.rfind('l');   // 反向查找

5. 总结

string 类是 C++ 中最重要的工具类之一,它:

  1. 解决了 C 字符串的安全性问题:自动内存管理,防止缓冲区溢出
  2. 提供了丰富的操作方法:查找、替换、分割等常用操作一应俱全
  3. 具有高效的实现:小字符串优化、写时拷贝等技术提升性能
  4. 支持现代 C++ 特性:与 STL 算法、范围 for 等完美配合

推荐的写法

string result;
result.reserve(5000); // 预先分配足够空间
for (int i = 0; i < 1000; ++i) {
    result += "data"; // 无重新分配
}

目录

  1. C++ string 类全面解析
  2. 1. 为什么学习 string 类?
  3. 1.1 C 语言中的字符串局限性
  4. 1.2 实际应用需求
  5. 2. 标准库中的 string 类
  6. 2.1 string 类基础
  7. 2.2 C++11 新特性:auto 和范围 for
  8. auto 关键字详解
  9. 范围 for 循环(Range-based for loop)
  10. 2.3 string 类常用接口详解
  11. 2.3.1 构造函数
  12. 2.3.2 容量操作
  13. 2.3.3 元素访问和遍历
  14. 2.3.4 修改操作
  15. 2.3.5 字符串操作
  16. 2.4 不同编译器下的 string 实现
  17. VS 下的 string 实现(小字符串优化)
  18. g++ 下的 string 实现(写时拷贝)
  19. 2.5 实战练习
  20. 示例 1:仅反转字母
  21. 示例 2:字符串相加(大数加法)
  22. 示例三:字符串最后一个单词的长度
  23. 3. string 类的模拟实现
  24. 3.1 浅拷贝问题
  25. 3.2 深拷贝实现
  26. 传统版 String 类
  27. 现代版 String 类(更优雅)
  28. 3.3 写时拷贝(Copy-On-Write)
  29. 4. 最佳实践和性能建议
  30. 4.1 字符串操作优化
  31. 4.2 选择合适的方法
  32. 5. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 服务端高并发分布式架构演进之路
  • 数据结构:八种常见排序算法详解
  • 人工智能大模型在医疗领域的应用进展与前景
  • AI 辅助 PCB 设计:效率革命与工程师角色重塑
  • AI 时代技术民主化:为何文科生或成最大受益者
  • AIGC 技术发展与应用实践
  • Python 异步编程与协程实战指南
  • Ubuntu 配置 Samba 实现跨系统文件共享
  • 大模型时代程序员如何应对 AI 挑战与角色转型
  • 基于微服务的智能家居物联网平台实践
  • Microi 吾码与 JavaScript 技术整合及应用实践
  • OSCP 实战:Net-NTLMv2 哈希传递与中继攻击详解
  • Python Flask 校园拍卖系统设计与实现
  • CppSharp 完全指南:5 步实现 C++ 到.NET 的自动化绑定
  • Python 与 Java:AI 项目技术选型对比
  • 基于 Java 的百度地图驾车路线规划服务开发指南
  • VSCode 配置 Python 开发环境实战指南
  • Python 使用 Turtle 库绘制动态樱花树及飘落效果
  • OpenCV C++ 快速入门与基础操作
  • Visual C++ 运行库完整方案与 DLL 依赖管理

相关免费在线工具

  • 加密/解密文本

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