一、学习 string 类的缘由
(一)C 语言字符串的局限
存储形式:以 '\0' 结尾的字符集合
操作问题:依赖 str 系列库函数,与字符串分离,不符合 OOP 思想
管理风险:底层空间需用户自行管理,易出现越界访问
(二)实际应用需求
OJ 题目:多以 string 类形式出题
工作场景:为追求简单、方便、快捷,优先使用 string 类,极少用 C 库字符串操作函数
(三)相关面试题预览
字符串转整形数字
字符串相加
二、标准库中的 string 类
(一)string 类基础认知
本质定义:表示字符序列的类,是 basic_string 模板类的实例(
typedef basic_string<char, char_traits, allocator> string
)接口特点:接口类似标准字符容器,额外添加单字节字符字符串操作特性
局限:无法处理多字节或变长字符序列(如 UTF-8),相关成员操作按字节进行
使用前提:包含
<string>
头文件,且使用using namespace std;
(二)常用接口详解
1. 类对象构造接口
函数名称 | 功能说明 |
---|---|
| 构造空的 string 类对象(空字符串) |
| 用 C 格式字符串构造 string 类对象 |
| 构造包含 n 个字符 c 的 string 类对象 |
| 拷贝构造函数,用已有 string 对象构造新对象 |
2. 容量操作接口
函数名称 | 功能说明 | 注意事项 |
---|---|---|
| 返回有效字符长度 | 与 |
| 返回有效字符长度 | 功能同 |
| 返回空间总大小 | 与实际可存储字符的空间相关 |
| 检测字符串是否为空,空返回 true,否则 false | 仅判断有效字符是否为空 |
| 清空有效字符 | 不改变底层空间大小 |
| 为字符串预留空间 | 参数小于当前底层空间大小时,不改变容量 |
| 将有效字符个数改为 n,多余空间用指定字符填充 | 增多字符时可能改变底层容量,减少字符时底层空间不变;无指定填充字符时用 0 填充 |
3. 访问及遍历操作接口
函数名称 | 功能说明 |
---|---|
| 返回 pos 位置的字符,const string 对象可调用 |
|
|
|
|
范围 for | C++11 新增,简洁遍历字符串 |
4. 修改操作接口
函数名称 | 功能说明 | 注意事项 |
---|---|---|
| 在字符串尾部插入字符 c | 仅能插单个字符 |
| 在字符串尾部追加字符串 | 可追加字符串 |
| 在字符串尾部追加字符或字符串 | 功能灵活,使用频率高 |
| 返回 C 格式字符串(以 '\0' 结尾) | 便于兼容 C 语言操作 |
| 从 pos 位置往后找字符 c,返回其位置 |
|
| 从 pos 位置往前找字符 c,返回其位置 | 与 |
| 从 pos 位置开始截取 n 个字符,返回新字符串 | 用于获取子串 |
5. 非成员函数接口
函数 | 功能说明 | 注意事项 |
---|---|---|
| 字符串拼接 | 传值返回,深拷贝效率低,尽量少用 |
| 输入运算符重载 | 用于读取字符串 |
| 输出运算符重载 | 用于输出字符串 |
| 获取一行字符串 | 可读取含空格的字符串,区别于 |
| 字符串大小比较 | 按字符 ASCII 码值比较 |
(三)不同编译器下的 string 结构
1. VS(32 位平台)
总占用空间:28 字节
内部结构:含联合体(存储字符串,长度 < 16 用固定数组,≥16 用堆空间)、size_t(存字符串长度)、size_t(存堆空间总容量)、指针(其他功能)
设计优势:短字符串(<16 字符)无需堆空间,效率高
2. g++(32 位平台)
总占用空间:4 字节(仅含一个指针)
实现方式:写时拷贝,指针指向堆空间,堆空间含空间总大小、有效长度、引用计数、字符串存储区
三、string 类模拟实现
(一)经典问题分析
默认拷贝构造与赋值重载的缺陷:浅拷贝导致多个对象共享同一块内存,销毁时重复释放引发崩溃
浅拷贝定义:也称位拷贝,仅拷贝对象值,共享资源,易引发访问违规
(二)深拷贝实现
1. 传统版写法
cpp
运行
class String {
public:
// 构造函数
String(const char* str = "");
// 拷贝构造函数
String(const String& s);
// 赋值运算符重载
String& operator=(const String& s);
// 析构函数
~String();
private:
char* _str;
};
构造:为字符串分配空间(含 '\0'),拷贝字符
拷贝构造:为新对象独立分配空间,拷贝源对象字符串
赋值重载:先判断是否自赋值,再分配新空间、拷贝字符、释放原空间
析构:释放空间,置空指针
2. 现代版写法
cpp
运行
class String {
public:
// 构造函数
String(const char* str = "");
// 拷贝构造函数(利用临时对象交换资源)
String(const String& s);
// 赋值运算符重载(参数传值,利用临时对象交换资源)
String& operator=(String s);
// 析构函数
~String();
private:
char* _str;
};
拷贝构造:先将
_str
置空,创建临时对象,交换_str
与临时对象的_str
赋值重载:参数传值生成临时对象,交换
_str
与临时对象的_str
,临时对象销毁时释放原资源
(三)写时拷贝(了解)
实现原理:基于浅拷贝,增加引用计数记录资源使用者个数
引用计数操作:构造时计数为 1,新增对象时计数 + 1,对象销毁时计数 - 1;计数为 0 时释放资源
特点:读取时共享资源,修改时才拷贝资源,存在读取缺陷