c++ - c++ - 在赋值时调用析构函数
问题描述
我仍在学习 C++ 的基础知识,所以我可能没有正确的词汇来找到我的问题的答案,但我在任何地方都找不到提到的这个。
如果我有一个带有构造函数和析构函数的类,为什么在分配给类时会在新数据上调用析构函数?
例如:
#include <iostream>
class TestClass {
public:
int* some_data;
TestClass() {
std::cout << "Creating" << std::endl;
some_data = (int*)malloc(10*sizeof(int));
}
~TestClass() {
std::cout << "Deconstructing" << std::endl;
free(some_data);
}
TestClass(const TestClass& t) : some_data{t.some_data} {
std::cout << "Copy" << std::endl;
}
};
int main() {
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
}
打印:
Creating
Created once
Creating
Deconstructing
Created twice
Deconstructing
free(): double free detected in tcache 2
Aborted (core dumped)
因此,在调试器中执行此操作后,似乎在新创建的数据上调用了解构器,这让我感到困惑。不应该释放一次原始数据,然后在执行结束时释放新数据吗?似乎原始数据永远不会像这样被释放。
解决方案
您的对象拥有一个指向已分配内存的原始指针,但没有实现适当的复制构造函数来进行分配并复制指针后面的数据。正如所写,当您复制一个对象时,指针被复制,这样现在两个对象指向同一个地址(以及刚刚分配给对象的旧地址被泄露。)
当临时对象超出范围时,它会删除其指针,但副本 (foo) 仍指向它。当 foo 超出范围时,它会再次删除相同的指针,从而导致您看到这个双重释放错误。
如果你需要编写析构函数来清理,你几乎总是需要同时提供复制和赋值操作,或者禁用它们。
建议:
- 将指针保存在 a
std::unique_ptr
中,如果您尝试复制它,它将无法编译。这迫使你处理这个问题。此外,malloc 和 free 主要用于 C 或低级 C++ 内存管理。考虑改为使用 new 和 delete 进行分配。(unique_ptrdelete
默认使用, notfree
,你不能混合它们。) - 或者,删除复制构造函数和赋值运算符
- 另外,考虑当您想从 xvalue(临时或移动的左值)移动时,您可以从右侧窃取分配。所以这个类是移动构造函数和移动赋值的一个很好的候选。
推荐阅读
- tailwind-css - Tailwind 中切换 UI 状态的正确方法是什么?
- facebook - 使用 Facebook Live Comment API 时不显示姓名
- php - php 升级触发 sha256 的新警告
- numpy - 计算没有库的每个图像的直方图
- reactjs - 无法从 Firestore 中查询某些数据(反应)
- sqlite - TypeOrm - 无法在主函数之外创建连接
- android - iOS 在这里 SDK 说 Heavy Right 但显示 Heavy Left
- php - Doctrine 的模式工具更新命令中的奇怪行为
- java - 扫描仪键盘无法处理我的 ParkingCarSimulator Java 程序中的某个值
- .net-core - IdentityServer 返回 BadRequest invalid_grant