VS 和 GCC 下 string 结构的差异

VS 下 string 的结构
在 32 位平台下,VS 的 string 对象占用 28 字节,采用“小字符串优化”(SSO)设计:

  • 使用联合体 _Bxty 存储字符串:
    • 当字符串长度小于 16 时,直接使用内部的固定字符数组 _Buf 存储。
    • 当字符串长度 ≥16 时,从堆上动态分配空间,通过 _Ptr 指针管理。
  • 其他字段包括:
    • 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_backappend 时检查容量,不足时重新分配内存。
  • 深拷贝:拷贝构造和赋值运算符需重新分配内存并复制内容,避免浅拷贝问题。
  • 资源释放:析构函数需释放堆内存。

性能与设计对比

特性 VS (SSO) GCC (COW)
对象大小 28 字节(固定) 4 字节(仅指针)
存储策略 小字符串内嵌,大字符串堆分配 全部堆分配,共享内存
适用场景 频繁操作短字符串 频繁复制且少修改
线程安全 无需额外处理 需原子操作管理引用计数

注意事项

  • 现代编译器中,COW 实现逐渐被弃用(如 GCC5+ 默认禁用),因多线程性能问题。
  • C++11 后移动语义优化了字符串传递性能,减少深拷贝开销。

更多推荐