定位:零基础入门 + 刷题刚需 + 期末考点 + 秋招面试底层手撕全覆盖
特点:摒弃碎片化讲解,从「库使用」到「底层原理」再到「手写模拟源码」层层递进,所有考点、坑点、面试问答全部汇总,可直接背诵、作业提交、面试复盘。
一、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...)
扩容底层流程(背诵):
-
开辟一块更大的新堆内存
-
将旧内存数据拷贝到新内存
-
释放旧堆内存
-
更新指针指向新内存
考点 :扩容会导致迭代器、指针、引用全部失效。
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。