1. 从零理解C++ string的底层设计
在C++开发中,string是最基础也最常用的类之一。很多开发者虽然能熟练使用string的各种接口,但当被问到"string底层是如何实现的"时却常常语塞。今天我们就来彻底拆解string的底层原理,并手把手带你从零实现一个简易版string类。
1.1 string类的核心成员变量
一个基本的string类通常由三个核心成员变量构成:
cpp复制private:
char* _str; // 指向动态分配的字符数组
size_t _capacity; // 当前分配的存储空间大小
size_t _size; // 当前存储的字符数量(不包括结尾的'\0')
这种设计有几个关键优势:
- 动态内存管理:可以根据需要灵活调整存储空间
- 高效的长度获取:_size成员使得size()操作是O(1)复杂度
- 预留空间机制:_capacity允许我们实现内存预分配,减少频繁扩容
1.2 基础构造与析构实现
让我们先实现最基础的构造函数和析构函数:
cpp复制// 构造函数
string::string(const char* str)
:_size(strlen(str))
{
_str = new char[_size + 1]; // 多分配1字节存放'\0'
_capacity = _size;
strcpy(_str, str); // 拷贝传入的字符串
}
// 析构函数
string::~string()
{
delete[] _str; // 释放动态分配的内存
_str = nullptr; // 避免野指针
_size = 0;
_capacity = 0;
}
这里有几个需要注意的技术细节:
- 构造函数使用strlen计算初始长度,避免了手动计算
- new分配内存时多申请1字节用于存放字符串结束符'\0'
- 析构函数中先将指针置为nullptr是个好习惯,可以避免后续误用
注意:在实际项目中,构造函数还应该检查传入的str是否为nullptr,这里为了代码简洁省略了检查
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 深入string的拷贝控制
2.1 拷贝构造函数的实现
C++中的拷贝构造函数在以下场景会被调用:
- 用一个对象初始化另一个对象时
- 函数参数按值传递时
- 函数返回对象时
我们需要实现深拷贝来避免多个对象共享同一块内存:
cpp复制string::string(const string& s)
{
_str = new char[s._capacity + 1];
memcpy(_str, s._str, s._size + 1); // 拷贝内容包括'\0'
_size = s._size;
_capacity = s._capacity;
}
这里使用了memcpy而不是strcpy,因为:
- memcpy性能更高,直接按字节拷贝
- 即使字符串中包含'\0'也能正确拷贝
- 明确知道要拷贝的字节数(s._size + 1)
2.2 赋值运算符的重载
赋值运算符需要处理自我赋值的情况,并实现深拷贝:
cpp复制stri
