揭秘C++ STL中string类的内部实现机制
·
VS 和 GCC 下 string 结构的差异
VS 下 string 的结构
在 32 位平台下,VS 的 string 对象占用 28 字节,采用“小字符串优化”(SSO)设计:
- 使用联合体
_Bxty存储字符串:- 当字符串长度小于 16 时,直接使用内部的固定字符数组
_Buf存储。 - 当字符串长度 ≥16 时,从堆上动态分配空间,通过
_Ptr指针管理。
- 当字符串长度小于 16 时,直接使用内部的固定字符数组
- 其他字段包括:
size_t类型变量保存字符串长度。size_t类型变量保存堆空间的容量。- 一个额外指针用于其他用途。
- 总大小计算:16(
_Buf) + 4(长度) + 4(容量) + 4(指针) = 28 字节。
union _Bxty {
char _Buf[16]; // 小字符串存储
char* _Ptr; // 大字符串存储
};
GCC 下 string 的结构
GCC 的 string 对象占用 4 字节(32 位平台),采用“写时拷贝”(Copy-On-Write)机制:
- 仅包含一个指针,指向堆上的结构体
_Rep_base,该结构体包含:- 字符串有效长度(
_M_length)。 - 总容量(
_M_capacity)。 - 引用计数(
_M_refcount)。 - 实际存储字符数组的指针。
- 字符串有效长度(
- 通过引用计数实现共享内存,修改时触发深拷贝。
struct _Rep_base {
size_t _M_length;
size_t _M_capacity;
int _M_refcount;
char* _data; // 实际字符串存储
};
string 类的模拟实现关键点
核心设计
模拟实现 string 需要管理动态字符数组,重点实现以下功能:
- 深拷贝的构造、赋值和析构。
- 迭代器支持。
- 动态扩容机制。
代码结构示例
头文件 string.h 声明类接口,源文件 string.cpp 实现复杂函数:
// string.h
namespace bit {
class string {
public:
typedef char* iterator;
iterator begin() { return _str; }
iterator end() { return _str + _size; }
string(const char* str = "");
string(const string& s); // 深拷贝构造
~string();
size_t size() const { return _size; }
char& operator[](size_t pos);
private:
char* _str;
size_t _size;
size_t _capacity;
};
}
关键实现细节
- 动态扩容:在
push_back或append时检查容量,不足时重新分配内存。 - 深拷贝:拷贝构造和赋值运算符需重新分配内存并复制内容,避免浅拷贝问题。
- 资源释放:析构函数需释放堆内存。
性能与设计对比
| 特性 | VS (SSO) | GCC (COW) |
|---|---|---|
| 对象大小 | 28 字节(固定) | 4 字节(仅指针) |
| 存储策略 | 小字符串内嵌,大字符串堆分配 | 全部堆分配,共享内存 |
| 适用场景 | 频繁操作短字符串 | 频繁复制且少修改 |
| 线程安全 | 无需额外处理 | 需原子操作管理引用计数 |
注意事项
- 现代编译器中,COW 实现逐渐被弃用(如 GCC5+ 默认禁用),因多线程性能问题。
- C++11 后移动语义优化了字符串传递性能,减少深拷贝开销。
更多推荐

所有评论(0)