为什么析构函数挂起 [英] Why is destructor hanging
问题描述
以下代码运行正常.但是,当我在 GetValue
中启用 p =& b
时,代码失败调试声明失败".为什么?
Below code was working fine. However, when I enable p=&b
in GetValue
, the code failed "Debug Assertion Failed". Why?
class A{
int *p;
public:
A(){p=nullptr;}
~A(){if(p!=nullptr)delete p;}
void GetValue(int b);
};
void A::GetValue(int b){
*p=b;
//p=&b; this will cause destructor to hang, why?
}
int main(void){
A B;
B.GetValue(5);
}
推荐答案
首先,重要的是,您只能删除
使用 new
分配的内存.当前,您的类 A
存储未使用 new
分配的指针 p
,但是您要进行删除p
好像过去一样结果是未定义的行为,这意味着不能保证您的程序行为正确,并且应该会出现非常奇怪的错误.
Firstly, it is important that you only delete
memory that was allocated using new
. Currently, your class A
stores a pointer p
that isn't allocated using new
, but you do delete p
as if it had been. The result of this is undefined behavior, which means that your program is not guaranteed behave correctly, and very weird bugs should be expected.
第二,在函数 A :: GetValue(int b);
中,参数 b
是临时变量.调用 GetValue
时,将在调用堆栈上留出一定的空间以传递 b
的值,该值在函数的整个生命周期中都驻留在此.但是,在 GetValue
返回之后,那里的 b
不再存在.尽管C ++允许您将指向无效内存的指针存储在其中,但是您需要注意避免使用此类指针.
Secondly, in the function A::GetValue(int b);
, the parameter b
is a temporary variable. When GetValue
is called, some space is made on the call stack to pass b
's value, where it resides for the lifetime of the function. But after GetValue
returns, b
no longer exists there. While C++ allows you to store pointers to invalidated memory, you need to be careful to avoid using such a pointer.
要使您的课程 A
正常工作,需要进行一些修饰,但是我将尽一切努力进行解释.虽然目前在一个简单的 int
成员用来存储 int *
指针似乎没有多大意义,但我将继续使用该指针来帮助您理解,并让原始指针的管理成为一种学习练习.
To make your class A
work correctly takes a fair bit of touching up, but I'll try to explain as I go. While currently it doesn't seem to make much sense to store an int*
pointer where a simple int
member would do, I'll keep using the pointer to help your understanding, and let the managing of a raw pointer be a learning exercise.
大多数问题源自 A :: GetValue(int)
.在这里,您要存储临时变量的地址,并且要在需要 new
-ed指针的上下文中进行存储.相反,您应该确保正确分配内存,而不要存储指向瞬态参数 b
:
Most of the problems stem from A::GetValue(int)
. Here, you're storing the address of a temporary variable, and in a context where a new
-ed pointer is expected. You should instead make sure to correctly allocate memory, and not store a pointer to the transient parameter b
:
A::GetValue(int b){
if (p == nullptr){
// if p is null, it needs to be allocated before being written to
p = new int(b); // initialize the memory at p to have value b
} else {
// otherwise, p has already been allocated, and its old value can be overwritten
*p = b;
}
}
编写以下代码时,还会出现另一个更细微的问题:
Another more subtle problem arises when you write the following code:
A a1, a2;
a1.GetValue(13);
a2 = a1;
这些行之后将发生的是 a1
的 p
成员将被删除两次,从而导致更多未定义的行为.罪魁祸首是自动生成的副本分配运算符,它只是复制 p
按值从 a1
到 a2
.要解决此问题,您需要编写自己的副本分配运算符和副本构造函数,如下所示.复制构造函数有点复杂,因为要处理许多不同的情况.
What will happen after these lines is that the p
member of a1
will be deleted twice, causing yet more undefined behavior. The culprit is the automatically generated copy assignment operator, which simply copies p
from a1
to a2
by value. To fix it, you need to write your own copy assignment operator and copy constructor as follows. The copy constructor is a little complex because there are lots of different cases to be handled.
class A {
...
A(const A& other) : p(nullptr) {
if (other.p){
p = new int(*other.p); // allocate new int and initialize with other's value
}
}
A& operator=(const A& other){
if (p){
// if this is managing a pointer
if (other.p){
// if other is managing a pointer too, just copy value
*p = *other.p;
} else {
// otherwise, since other is null, delete and make this null
delete p;
p = nullptr;
}
} else {
// if this is not managing a pointer
if (other.p){
// other is managing a pointer, time to allocate
p = new int(*other.p);
}
// nothing needs to be done if both are null
}
}
The importance of doing this is explained in the Rule of Three.
这篇关于为什么析构函数挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!