C++ string 超全精讲 | 从零使用、底层原理、手搓简易string、高频考点、易错点、面试手撕

定位:零基础入门 + 刷题刚需 + 期末考点 + 秋招面试底层手撕全覆盖

特点:摒弃碎片化讲解,从「库使用」到「底层原理」再到「手写模拟源码」层层递进,所有考点、坑点、面试问答全部汇总,可直接背诵、作业提交、面试复盘。

一、string 核心认知(必背概念)

1.1 什么是 C++ string?

string 是 C++ 标准库提供的字符串类(std::string),专门用于处理字符序列。

本质是:动态字符数组 + 自动内存管理 + 大量封装工具方法

完全替代 C 语言 char* 字符串,解决了 C 字符串的致命缺陷:

  • C 语言 char*:固定长度、容易越界、手动管理内存、无内置方法、无法直接比较、无法直接拼接

  • C++ string:动态扩容、自动析构、支持运算符重载、丰富API、安全易用

1.2 核心底层本质(面试必考)

std::string 底层并不是简单的静态数组,而是:

动态堆区 char 数组 + 长度变量(size) + 容量变量(capacity)

三大核心属性:

  • size:当前有效字符个数(真实长度)

  • capacity:当前已开辟的内存容量(最大可存字符数)

  • data:指向堆区字符数组的指针

关键区分:size <= capacity,扩容只扩 capacity,不影响逻辑 size。

1.3 空字符 '\0' 机制(超级易错)

  • C 语言字符串:必须以 \0 结尾,靠 \0 判断结束

  • C++ string:内部记录 size 长度,不靠 \0 判定结束

  • string 末尾自动补 \0(为了兼容C接口),但逻辑遍历完全以 size 为准

二、string 基础使用大全(刷题必备API)

2.1 四种创建初始化方式

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

int main()
{
    // 1. 空字符串初始化
    string s1;            

    // 2. 常量字符串初始化
    string s2("hello");   

    // 3. 直接赋值初始化(最常用)
    string s3 = "world";  

    // 4. n个相同字符初始化
    string s4(5, 'a');    

    return 0;
}

2.2 常用属性获取(考试高频)

cpp 复制代码
string s = "hello";

cout << s.size();    // 有效字符长度 5
cout << s.length();  // 与size完全等价
cout << s.capacity();// 当前内存容量
cout << s.empty();   // 是否为空:空返回1,非空0

考点:size() / length() 完全一致,string 无区别;容器统一用 size()。

2.3 字符串拼接(三种方式)

cpp 复制代码
string s1 = "abc";
string s2 = "123";

// 1. + 直接拼接(最常用)
string s3 = s1 + s2;

// 2. append 拼接
s1.append(s2);

// 3. 追加单个字符
s1.push_back('x');

2.4 查找函数(刷题核心)

cpp 复制代码
string s = "abcdefg";

// 1. 从前往后查找
size_t pos = s.find("cd");   

// 2. 从后往前查找
size_t pos2 = s.rfind("fg"); 

// 查找失败返回:string::npos
if (pos == string::npos)
    cout << "未找到";

超级考点 :find 失败返回 string::npos,必须判断,不判断会报错。

2.5 截取子串 substr(必考)

cpp 复制代码
string s = "0123456";

// 参数1:起始下标  参数2:截取长度
string sub = s.substr(1, 3); 
// sub = "123"

2.6 插入、删除、清空

cpp 复制代码
string s = "abcde";

s.insert(1, "666");  // 在下标1插入字符串
s.erase(2, 2);       // 从下标2删除2个字符
s.clear();           // 清空字符串 size=0

2.7 字符串比较

string 重载了 == != > < >= <=,直接字典序比较。

cpp 复制代码
string a = "abc";
string b = "abd";
if (a < b) { } // 直接比较字典序

三、string 底层核心考点(面试必问)

3.1 扩容机制(秋招高频)

string 是动态字符数组,容量不够时自动扩容:

  • VS编译器:首次15,之后每次翻倍

  • GCC编译器:倍增扩容(1、2、4、8、16...)

扩容底层流程(背诵)

  1. 开辟一块更大的新堆内存

  2. 将旧内存数据拷贝到新内存

  3. 释放旧堆内存

  4. 更新指针指向新内存

考点 :扩容会导致迭代器、指针、引用全部失效

3.2 深浅拷贝

3.2.1 浅拷贝问题(C语言 char*)

仅拷贝指针地址,多个变量指向同一块堆内存,析构重复释放崩溃。

3.2.2 string 自带深拷贝

std::string 默认实现深拷贝:赋值/拷贝构造时,开辟新堆内存,复制内容,各自独立内存,互不干扰。

cpp 复制代码
string a = "123456";
string b = a; // 深拷贝,b拥有独立内存

3.3 npos 常量考点

string::npos 是 size_t 类型的最大值(无符号)

致命坑点:不能用 int 接收 find 返回值,必须用 size_t。

3.4 c_str() 接口考点

s.c_str():将 C++ string 转为 C 语言 const char*,自带末尾 \0。

四、手搓简易 string 类(面试手撕核心 · 完整版)

严格按照面试要求:手写简易string,包含构造、拷贝构造、赋值重载、析构、扩容、拼接、获取长度,全程深拷贝、无内存泄漏、逐行注释。

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

/**
 * @brief 手写简易C++ String类(面试手撕标准版)
 * 实现核心:动态堆内存 + 深拷贝 + 运算符重载 + 基础接口
 * 模拟std::string最核心底层机制
 */
class MyString
{
private:
    char* data;    // 指向堆区字符数组
    size_t size;   // 当前有效长度
    size_t capacity;// 当前容量

    // 扩容函数:底层动态扩容核心
    void expand(size_t newCap)
    {
        if (newCap <= capacity) return;

        // 1. 开辟新内存
        char* newData = new char[newCap + 1];
        // 2. 拷贝旧数据
        strcpy(newData, data);
        // 3. 释放旧内存
        delete[] data;
        // 4. 更新指针与容量
        data = newData;
        capacity = newCap;
    }

public:
    // 1. 无参构造:空字符串
    MyString()
    {
        size = 0;
        capacity = 15;
        data = new char[capacity + 1];
        data[0] = '\0';
    }

    // 2. 常量字符串构造
    MyString(const char* str)
    {
        size = strlen(str);
        capacity = size;
        data = new char[capacity + 1];
        strcpy(data, str);
    }

    // 3. 拷贝构造【深拷贝 面试必考】
    MyString(const MyString& other)
    {
        size = other.size;
        capacity = other.capacity;
        // 新开内存,不共享堆区
        data = new char[capacity + 1];
        strcpy(data, other.data);
    }

    // 4. 赋值运算符重载【深拷贝 防止浅拷贝崩溃】
    MyString& operator=(const MyString& other)
    {
        // 防止自赋值
        if (this == &other)
            return *this;

        // 释放自身旧内存
        delete[] data;

        // 拷贝新数据
        size = other.size;
        capacity = other.capacity;
        data = new char[capacity + 1];
        strcpy(data, other.data);

        return *this;
    }

    // 5. 析构函数:释放堆内存
    ~MyString()
    {
        delete[] data;
        data = nullptr;
    }

    // 6. += 字符串拼接重载
    MyString& operator+=(const char* str)
    {
        size_t addLen = strlen(str);
        // 容量不足则扩容
        if (size + addLen > capacity)
        {
            expand((size + addLen) * 2);
        }
        // 拼接字符串
        strcat(data, str);
        size += addLen;
        return *this;
    }

    // 7. 获取长度
    size_t getSize() const
    {
        return size;
    }

    // 8. 转为C语言字符串
    const char* c_str() const
    {
        return data;
    }
};

// 测试主函数
int main()
{
    MyString s1("hello");
    MyString s2 = s1;    // 测试拷贝构造
    MyString s3;
    s3 = s1;             // 测试赋值重载

    s1 += " world";      // 测试拼接扩容
    cout << s1.c_str() << endl;

    return 0;
}

面试满分话术 :手写string核心难点在于深拷贝实现动态扩容机制,必须手动释放旧内存、开辟新内存、防止自赋值、杜绝浅拷贝内存问题。

五、string 高频易错点(考试/刷题大坑)

5.1 下标访问不检查越界

si 直接访问内存,不做越界检查,越界直接内存脏读/崩溃。

5.2 find返回值不能用int接收

npos 是无符号最大值,int接收会变成负数,判断失效。必须用 size_t。

5.3 扩容导致迭代器失效

string 扩容后,原指针、迭代器、引用全部失效,需要重新获取。

5.4 清空size不清空capacity

clear() 只清空有效字符,容量不变,内存不释放。

5.5 string 与 char* 本质区别

  • char* 是指针变量,不管理内存

  • string 是类对象,自动管理堆内存生命周期

六、面试满分问答

6.1 为什么string插入新字符默认不崩溃?

因为string底层是动态堆数组,容量不足自动扩容,重新开辟内存、拷贝数据、释放旧内存,实现动态增长。

6.2 string为什么不需要手动释放内存?

string 对象生命周期结束时,析构函数自动释放堆内存,无内存泄漏。

6.3 深拷贝和浅拷贝在string中的体现?

默认拷贝构造和赋值重载都是深拷贝,每个string拥有独立堆内存,赋值后互不影响,避免重复析构崩溃。

6.4 string的size和capacity区别?

size是当前有效字符数,capacity是已开辟内存最大容量;size变化不影响capacity,扩容只提升capacity。

七、全文总结

1、std::string 是 C++ 封装的动态字符串类,底层为堆字符数组,自带 size、capacity、动态扩容机制;

2、完全替代 C 语言 char*,自带深拷贝、自动内存管理、大量工具API;

3、核心考点集中在:扩容机制、深浅拷贝、npos、迭代器失效、size/capacity区别;

4、面试可手撕简易string,核心难点为:深拷贝构造、赋值重载、手动扩容、内存释放;

5、刷题重点掌握:find、substr、append、erase、empty 五大高频API。

相关推荐
Chase_______15 小时前
【Java基础】5 / 2 为什么等于 2?整数除法、取余和 floorMod 一次讲清
java·开发语言
fish_xk15 小时前
c++11(二)
java·前端·c++
foundbug99915 小时前
实现MATLAB滚动轴承故障诊断
开发语言·matlab
gihigo199815 小时前
matlab实现三维四面体单元的有限元解法
开发语言·matlab
fengfuyao98515 小时前
Chen混沌系统 — 基于自适应控制的MATLAB仿真实现
开发语言·机器学习·matlab
张小姐的猫15 小时前
【Linux】多线程实战 —— 日志类 | 策略模式
linux·运维·服务器·c++·bash·策略模式
yong999015 小时前
MATLAB的卷积码的编码和译码的实现
开发语言·matlab
闻缺陷则喜何志丹15 小时前
P8134 [ICPC 2020 WF] Opportunity Cost|普及+
c++·算法·洛谷
不会C语言的男孩15 小时前
C++ Primer Plus 第2章:开始学习C++
开发语言·c++