
🔥草莓熊Lotso:个人主页
❄️个人专栏:《C++知识分享》《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受。
🎬博主简介:

在 C++11 引入移动语义之前,std::string 的实现依赖于深拷贝、写时拷贝(Copy-On-Write)等经典内存管理策略,而swap 操作则是提升性能的关键优化点。本文聚焦 C++11 之前string的三大swap实现与拷贝机制,带你理解早期字符串类设计的核心智慧。

字符串的拷贝是内存管理的核心场景,错误的拷贝实现会导致内存泄漏或程序崩溃,而优化的拷贝策略则能显著提升性能。
浅拷贝是最直观但危险的拷贝方式,其本质是仅复制指针地址而非实际数据,导致多个对象共享同一块内存。
问题展现:
namespace Lotso
{
class string
{
private:
char* _str;
size_t _size;
public:
// 构造函数:分配内存并初始化
string(const char* str = "") : _size(strlen(str))
{
_str = new char[_size + 1];
strcpy(_str, str);
}
// 未实现深拷贝,使用编译器默认的浅拷贝
~string()
{
delete[] _str;
} // 析构时释放内存
};
// 崩溃
int main()
{
string s1("hello");
string s2 = s1; // 浅拷贝:s1._str 与 s2._str 指向同一块内存
return 0;
// 析构时:s2 先释放内存,s1 再释放已被释放的内存→两个释放同一块内存程序崩溃
}
};问题根源:就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。


可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父
母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

深拷贝通过为每个对象分配独立内存并复制数据,彻底避免资源共享,是最安全的拷贝方式。

class String
{
public:
String(const char* str = "")
{
// 构造String类对象时,如果传递nullptr指针,可以认为程序非
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
if (this != &s)
{
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
}
return *this;
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};优势:每个对象拥有独立内存,析构时不会冲突;
缺点:每次拷贝都需分配内存和复制数据,效率较低(O (n) 时间复杂度)
class String
{
public:
String(const char* str = "")
{
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(nullptr)
{
String strTmp(s._str);
swap(_str, strTmp._str);
}
// 对比下和上面的赋值那个实现比较好?
String& operator=(String s)
{
swap(_str, s._str);
return *this;
}
/*
String& operator=(const String& s)
{
if(this != &s)
{
String strTmp(s);
swap(_str, strTmp._str);
}
return *this;
}
*/
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};核心逻辑:借助临时对象的深拷贝,通过swap将临时对象的资源 “转移” 到当前对象,临时对象析构时会自动释放原对象的旧内存,既避免数据丢失,又简化代码。

C++ STL string的Copy-On-Write技术 | 酷 壳 - CoolShell https://coolshell.cn/articles/1443.html
C++面试中string类的一种正确写法 | 酷 壳 - CoolShell STL 的string类怎么啦?_string拥有庞大字符串 c++-CSDN博客
string类的成员swap直接交换底层资源(指针、大小、引用计数等),不涉及数据拷贝,是效率最高的实现。
代码实现:
class String {
public:
void swap(String& s) {
// 交换核心成员,无数据拷贝
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
private:
char* _str;
size_t _size;
size_t _capacity;
};优势:
C++ 标准库提供的全局std::swap模板,默认逻辑是 “拷贝 - 覆盖 - 析构”,对字符串而言效率极低。
代码实现:
template <class T>
void swap(T& a, T& b)
{
T c(a);// 深拷贝 a 到临时对象(O(n)
a = b; //深拷贝 b 到 a(O(n))
b = c;// 深拷贝临时对象到 b(O(n))
}问题:对长字符串而言,三次深拷贝会导致 O (n) 时间复杂度,性能极差。
为解决默认swap的低效问题,标准库会为std::string提供模板特化,直接调用成员swap,将效率提升至 O (1)。
代码实现:
inline void swap(string& a, string& b)
{
//还是调的库里的
a.swap(b);
}优势:兼顾通用性与效率,在模板函数(如排序算法)中使用试探大std::swap时,仍能享受高效交换。
在 C++11 之前,string的设计围绕 “内存效率” 与 “安全性” 展开:
拷贝机制的选择:
swap 实现的最优实践:
往期回顾:
《告别 “会用不会讲”:C++ string 底层原理拆解 + 手撕实现,面试 / 开发都适用》
结语:回溯 C++98/03 string 的拷贝与 swap 机制,既是理解早期内存管理智慧的钥匙,也为掌握现代 C++ 容器设计打下根基。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど