C++ 为什么在使用赋值时没有为堆栈变量调用析构函数?

C++ 为什么在使用赋值时没有为堆栈变量调用析构函数?,c++,destructor,C++,Destructor,这个愚蠢的代码被剪掉已经花了我2个小时,我不明白为什么第一个元素的析构函数,大小为7的元素,没有被调用?为新uint16\u t[7]分配的内存会发生什么变化 #include <iostream> using namespace std; struct Node { Node(uint16_t n) : p(new uint16_t[n]) { cout<<"Constructed with size= "<<n<<"

这个愚蠢的代码被剪掉已经花了我2个小时,我不明白为什么第一个元素的析构函数,大小为7的元素,没有被调用?为
新uint16\u 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;
}
我不明白为什么第一个元素的析构函数,大小为7的元素,没有被调用

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


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


在对象上指定时,指向内存的指针丢失。这称为内存泄漏。

此代码:
nd1=Node(3)与您的期望不符

它不会将
nd1
替换为
节点(3)
并杀死旧的
nd1

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

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


要解决这个问题,您必须实现所谓的五规则或零规则

最简单的是零法则。只需使用
unique\u 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;
}
struct节点
{
节点(uint16_t n):p(std::make_unique(n)){

cout双自由是因为您的类违反了3/5/0规则:内置运算符=()将对您的类进行成员级复制,最终复制指针的值。@drescherjm实际上我所期望的是当我使用=运算符时,首先是节点(7)的析构函数调用,然后进行赋值。但显然不是。运算符=()没有调用节点(7)的析构函数。此代码:nd1=节点(3);与您期望的不符-->没错。我希望有一系列操作,首先我们需要去掉现有的值。然后进行新的赋值。您忘记了从析构函数中删除现在多余的
delete[]p;
。另外,对于C++14或更高版本,它更安全(对于具有其他状态的可能更复杂的代码进行初始化)将初始值设定项从
p(new uint16_t[n])
更改为
p(std::make_unique(n))
,因为它保证执行的顺序是在分配之后,但在
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;
}