【问题标题】:Why is the destructor not called for stack variable when using assignement?为什么在使用赋值时不为堆栈变量调用析构函数?
【发布时间】:2020-01-24 05:40:35
【问题描述】:

这个愚蠢的代码剪断已经花了我 2 个小时,我不明白为什么第一个元素的析构函数,大小为 7 的那个,没有被调用?分配给new uint16_t[7] 的内存会怎样?

#include <iostream>

using namespace std;

struct Node
{
    Node(uint16_t n) : p(new uint16_t[n]) {
        cout<<"Constructed with size= "<<n<<", memory addr: "<<(p)<<endl;
        for(uint16_t i=0; i<n; i++) p[i] = n;
    }

    ~Node() {
        cout<<"Destructor for p[0] = "<< *p <<" with memory addr: "<<p<<endl;
        delete[] p;
    }

    uint16_t *p;
};

int main()
{
    {
        Node nd1(7);
        {
            nd1 = Node(3);
            cout << "1st place holder" << endl;
        }
        cout << "2nd place holder" << endl;
    }

    return 0;
}

输出是

Constructed with size= 7, memory addr: 0x158cc20                                                                                                                                   
Constructed with size= 3, memory addr: 0x158cc40                                                                                                                                   
Destructor for p[0] = 3 with memory addr: 0x158cc40                                                                                                                                
1st place holder                                                                                                                                                                   
2nd place holder                                                                                                                                                                   
Destructor for p[0] = 0 with memory addr: 0x158cc40                                                                                                                                
*** Error in `./a.out': double free or corruption (fasttop): 0x000000000158cc40 ***                                                                                                
Aborted (core dumped) 

【问题讨论】:

  • 双重免费是因为你的班级违反了3/5/0的规则:https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three
  • 内置 operator=() 将对您的类进行成员明智的复制,最终复制指针的值。
  • @drescherjm 实际上,我期望的是,当我使用 = 运算符时,首先调用 Node(7) 的析构函数,然后进行赋值。但显然不是。
  • Node(7) 的析构函数未被 operator=() 调用

标签: c++ destructor


【解决方案1】:

我不明白为什么第一个元素的析构函数,即大小为 7 的那个,没有被调用?

调用。事实上,正是那个析构函数导致程序崩溃。程序的行为是未定义的,因为析构函数删除了先前被临时对象的析构函数删除的相同指针值。并且在此之前,它通过那个无效的指针间接。


分配给新 uint16_t[7] 的内存会发生什么变化?

当您分配对象时,指向内存的指针丢失了。这称为内存泄漏。

【讨论】:

    【解决方案2】:

    此代码:nd1 = Node(3); 不符合您的预期。

    nd1替换为Node(3)并杀死旧的nd1

    它的作用是创建一个新的临时节点实例Node(3),并将每个成员的值复制到nd1。所以临时和nd1 都包含一个指向同一个地址的指针。此时,您泄漏了在程序开始时分配的内存nd1,因为没有指针指向它,但您没有删除它。

    当临时死掉时,nd1 指向死内存。当nd1 在第二个} 处运行其析构函数时,它会再次删除相同的指针,从而导致您的错误。


    要解决此问题,您必须实施所谓的五规则或零规则。

    最简单的是零规则。只需使用unique_ptr 即可按预期进行破坏:

    struct Node
    {
        Node(uint16_t n) : p(std::make_unique<uint16_t[]>(n)) {
            cout<<"Constructed with size= "<<n<<", memory addr: "<<(p)<<endl;
            for(uint16_t i=0; i<n; i++) p[i] = n;
        }
    
        std::unique_ptr<uint16_t[]> p;
    };
    
    int main()
    {
        {
            Node nd1(7);
            {
                nd1 = Node(3); // assignement destroys the old buffer
                cout << "1st place holder" << endl;
            }
            cout << "2nd place holder" << endl;
        }
    
        return 0;
    }
    

    【讨论】:

    • “此代码:nd1 = Node(3); 不是您所期望的。”--> 完全正确。我期待一系列操作,首先我们需要摆脱现有的价值。然后做一个新的任务。
    • 你忘了从析构函数中删除现在多余的delete[] p;。此外,对于 C++14 或更高版本,将初始值设定项从 p(new uint16_t[n]) 更改为 p(std::make_unique&lt;uint16_t[]&gt;(n)) 更安全(对于可能更复杂的具有其他状态的代码进行初始化),因为它保证执行按顺序发生,以便在未排序的代码中出现异常在分配之后,但在 unique_ptr 初始化之前,不会发生附近它,从而导致泄漏。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2019-11-16
    • 1970-01-01
    • 2020-10-29
    • 2016-03-02
    • 2015-08-21
    相关资源
    最近更新 更多