当通过引用传递包含指针成员的类对象时,为什么要多次调用析构函数?我该如何更正
Why is the destructor called multiple times when a class object containing a pointer member is passed by reference? How do I correct this?
我有一个形式的类
class A
{
int **a;
//other members
}
在某些函数中,我通过引用传递A类对象,比如obj
void func(A &o); //function declaration
和
func(std::ref(obj)); //function call
然而,我得到了以下错误-双重免费或损坏(!prev)
根据链接的问题,这是因为指针成员的析构函数被多次调用,这是复制时多次调用复制构造函数的结果,导致试图释放已经释放的内存。然而,当我通过引用传递完整的对象时,为什么要发生这种情况?难道不应该简单地复制整个对象的地址吗?
以上面链接和这里给出的形式实现复制构造函数的建议没有帮助,因为它们涉及在复制对象时分配新的内存量,而我希望通过引用传递对象,从而传递指针成员。
我看了这个和这个,潜在的重复,但它们并没有解决我的问题。
基于其他一些答案,我还尝试将析构函数实现为
~A()
{
delete[] a;
}
或
~A()
{
if(a)
{
delete[] a;
}
}
但都没有解决问题。
尝试A(A const&)=delete;
和A(A&&o):a(o.a){a=nullptr;}
。这将删除您的复制ctor并写入一个未分配的移动ctor。
如果您试图复制类的实例,则已删除的副本ctor会给您带来编译时错误。在你的情况下,这会导致崩溃,这似乎是个好主意。
只要返回值是隐式或显式(std::move
)从中移动的,那么这个move ctor将允许您从函数安全地返回类的实例。
同时考虑
A& operator=(A&&o) {
if (this==&o) return *this;
std::swap(a,o.a);
delete[] o.a;
o.a=nullptr;
return *this;
}
A& operator=(A const&o)=delete;
这使您可以从A
移动的另一个分配给A
。
在编写析构函数时,应该处理移动/复制分配/构造的事实被称为五规则。
事实上,你应该避免写任何一个,这被称为零规则。
为了避免写入它们,请将a
替换为unique_ptr<int[]>
。现在,它为您生成移动操作和析构函数,并删除复制操作,所有操作都是自动的。
我解决了这个问题。我从一个调用复制构造函数的函数返回了该类的一个对象。我忽略了对象将按值传递的事实。
@IgorTandetnik的评论在一定程度上回答了这个问题,它证实了我观察到的问题没有发生的原因,只是我忽略了某个地方发生的传递值。
这个来自@Slava评论中提供的链接的答案对于找出问题的根源非常有用。
- 为什么使用 "this" 指针调用派生成员函数?
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 为什么 std::unique 不调用 std::sort?
- C++为什么尽管我调用了void函数,它却不起作用
- 为什么使用 P/Invoke 调用 dll 时,某些计算机中的 LoadLibrary 失败?
- 为什么我不能在 C++ 中的特定函数重载中调用同一函数的任何其他重载?
- 为什么我的 IExtractIcon 处理程序没有被调用?
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 为什么调用堆栈数组会导致内存泄漏
- 循环中的条件:为什么每次都调用strlen(),而vector.size()只调用一次
- 通过引用传递-为什么要调用这个析构函数
- WIN32:C++,为什么在WM_CLOSE上调用Messagebox函数程序正在冻结
- 为什么在 x64 中忽略__stdcall调用约定?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 为什么 zlib 放气初始化调用一次不起作用?
- 为什么默认复制函数在按值发送参数时不调用?
- 如果整个应用程序是虚拟映射的,为什么 new 会进行系统调用?
- Java用JNI调用C++:为什么JNIEnv指针被取消引用两次
- 在移动构造函数的缺席中,复制构造函数被调用?为什么呢?