C++自定义String类析构函数两次调用问题咨询及内存安全性疑问
嘿,我来帮你理清这个问题——先直接给你核心结论,再一步步拆解你的代码问题和解决方案。
核心疑问:析构函数调用两次≠内存错误
首先要明确:析构函数被调用两次本身不是bug,关键是看每次调用是否在释放自己合法拥有的内存。只要不同对象释放的是各自分配的内存块,没有出现同一块内存被多次delete的情况,程序就是安全的。
你用unique_ptr后虽然析构还是调用两次,但因为unique_ptr严格保证了内存的唯一所有权,不会让多个对象指向同一块堆内存,所以不会触发double free这类致命错误,这是完全正常的。
你的代码里两次析构的来源
先看你的主函数:
int main() { String a = "asdc"; a = "ads"; cin.get(); }
这两次析构调用其实来自两个不同的对象:
- 第一次析构:当执行
a = "ads"时,字符串字面量"ads"会被临时构造为一个String对象(调用你的String(const char*)构造函数)。赋值操作完成后,这个临时对象就没用了,会被自动销毁,触发第一次~String()调用。 - 第二次析构:main函数执行结束时,局部变量
a离开作用域,被销毁,触发第二次析构。
之前没加空构造函数时不是没这个情况,只是可能你没注意到临时对象的析构输出而已——临时对象的创建和销毁一直都存在。
但这里还有个隐藏的严重问题:你的String类没有重载赋值运算符operator=!当你执行a = "ads"时,编译器会自动生成一个默认的赋值运算符,它只会做浅拷贝:把临时对象的m_Buffer指针直接复制给a.m_Buffer。这会导致两个问题:
- 原
a的m_Buffer指向的内存没有被释放,直接造成内存泄漏。 - 临时对象销毁时会
delete[]这个指针,之后a的m_Buffer就变成了野指针;当a销毁时再次delete[]这个野指针,就会触发double free错误!
你说用unique_ptr解决了问题,本质是unique_ptr禁止了拷贝赋值(默认删除了拷贝构造和拷贝赋值),或者你用了移动语义——它通过独占内存所有权的方式,从根源上避免了浅拷贝带来的重复释放问题。
用五五法则(Rule of Five)修复你的String类
因为你的类管理了堆内存(m_Buffer),所以必须手动实现C的五五法则(C11及以后),也就是五个特殊成员函数:
- 构造函数(你已经实现了)
- 析构函数(你已经实现了)
- 拷贝构造函数(你已经实现了)
- 拷贝赋值运算符(你缺失的关键)
- 移动构造函数(可选但能提升效率)
- 移动赋值运算符(可选但推荐)
给你补上拷贝赋值运算符的实现:
String& operator=(const String& other) { // 先处理自赋值的边界情况 if (this == &other) return *this; // 先释放当前对象的旧内存,避免泄漏 delete[] m_Buffer; // 分配新内存并拷贝数据 m_Size = other.m_Size; m_Buffer = new char[m_Size + 1]; memcpy(m_Buffer, other.m_Buffer, m_Size + 1); return *this; }
如果想支持更高效的移动语义(避免临时对象的内存拷贝),可以添加移动构造和移动赋值:
// 移动构造函数:接管源对象的内存 String(String&& other) noexcept : m_Buffer(other.m_Buffer), m_Size(other.m_Size) { // 把源对象置为安全状态,防止它析构时释放我们刚接管的内存 other.m_Buffer = nullptr; other.m_Size = 0; } // 移动赋值运算符 String& operator=(String&& other) noexcept { if (this == &other) return *this; delete[] m_Buffer; m_Buffer = other.m_Buffer; m_Size = other.m_Size; other.m_Buffer = nullptr; other.m_Size = 0; return *this; }
这样修改后,当你执行a = "ads"时,临时对象会被移动赋值给a,既不会内存泄漏,也不会出现野指针和重复释放的问题。
最后总结一下
- 析构函数调用两次是正常场景(比如临时对象的销毁),只要每个对象只释放自己合法拥有的内存,就不会有问题。
- 管理堆内存的自定义类必须遵循五五法则,否则会出现内存泄漏、double free等经典内存错误。
unique_ptr是C++标准库提供的智能指针,它通过独占内存所有权的方式帮你自动管理内存,避免手动操作原始指针的各种坑,是替代原始指针的优先选择。
内容的提问来源于stack exchange,提问作者Mustafa Alzuriqi

