为什么析构函数挂起 [英] Why is destructor hanging

查看:102
本文介绍了为什么析构函数挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码运行正常.但是,当我在 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
        }
    }

三人规则中对此进行了解释.a>.

The importance of doing this is explained in the Rule of Three.

这篇关于为什么析构函数挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆