两个程序之间的区别?

时间:2016-10-19 08:51:42

标签: c++ c++11 destructor

我使用list-node创建了一个堆栈类。 但一个工作正常,另一个总是崩溃。 两个程序有什么区别? 我thimk destructor破坏

#include<iostream>
using namespace std;

class Stack
{
public:
    int pop() {
        data = next->data;
        auto tmp = next;
        next = next->next;
        delete tmp;
        return data;
    }
    void push(int n) {
        Stack* p = new Stack();
        p->data = n;
        p->next = next;
        next = p;
    }
    virtual ~Stack() {
        free();
    }
    void free() {
        while(next) pop();
    }

protected:
    int data;
    Stack* next = nullptr;
};
int main()
{
    Stack s;
    s.push(1);
    s.push(2);
    //s.free();
}

以上程序总是崩溃..

#include<iostream>
using namespace std;

class Stack
{
public:
    int pop() {
        data = next->data;
        auto tmp = next;
        next = next->next;
        delete tmp;
        return data;
    }
    void push(int n) {
        Stack* p = new Stack();
        p->data = n;
        p->next = next;
        next = p;
    }
    virtual ~Stack() {
    //  free();
    }
    void free() {
        while(next) pop();
    }

protected:
    int data;
    Stack* next = nullptr;
};
int main()
{
    Stack s;
    s.push(1);
    s.push(2);
    s.free();
}

这个程序运行正常。

两者有什么区别?

2 个答案:

答案 0 :(得分:1)

当你在tmp中删除pop时,你会调用Stack的析构函数,在你的第一个代码中加倍free

以下是推送1和2后的堆栈状态(p1p2Stack中创建的新push个对象:

   |    data       |     next
-------------------------------
s  | uninitialized |      p1
p1 |      1        |      p2
p2 |      2        |    nullptr

现在,如果您在第一段代码中销毁s(忽略data会发生什么):

  • s.free已执行。
  • s.nextp1,因此s.pop已执行。
  • tmp变为s.next,即p1
  • s.next变为s.next->next,即p1->next,即p2
  • tmp,即p1,将被删除,因此会调用p1的析构函数。此时,我们处于以下状态:

       |    data       |     next
    -------------------------------
    s  | uninitialized |      p2
    p1 |      1        |      p2
    p2 |      2        |    nullptr
    
  • p1的析构函数调用free,调用pop(因为p1->nextp2)。

  • p2已通过tmp删除。
  • 返回s的析构函数,其状态如下:

       |    data       |     next
    -------------------------------
    s  | uninitialized |      p2 (dangling)
    p2 |      2        |    nullptr
    

在您的第二个代码中,p1的析构函数调用free,并且不会使p2成为悬空指针,因此代码“有效”细”。

您应该像这样修改pop

int pop() {
    data = next->data;
    auto tmp = next;
    next = next->next;
    /***********************/
    next->next = nullptr;
    /***********************/
    delete tmp;
    return data;
}

顺便说一句,如果您使用的是C ++ 11,则应该使用托管指针重新设计代码。这样的事情:

#include<iostream>
#include<memory>
using namespace std;

class Stack
{
public:
    int pop() {
        data = next->data;
        next = move(next->next);
        return data;
    }
    void push(int n) {
        auto p = make_unique<Stack>();
        p->data = n;
        p->next = move(next);
        next = move(p);
    }
    void free() {
        while(next) pop();
    }

protected:
    int data;
    unique_ptr<Stack> next;
};
int main()
{
    Stack s;
    s.push(1);
    s.push(2);
}

make_unique<Stack>()为C ++ 14,您可以将其替换为unique_ptr<Stack>(new Stack)

答案 1 :(得分:1)

首先,您的pop已损坏 - delete tmp;以递归方式删除以tmp开头的整个子包,而不仅仅是顶级节点。
tmp的析构函数也将free();,依此类推。)
这会导致双重删除。

您的第二个版本会泄漏内存,除非您记得在完成堆栈后致电free()

您可以保持析构函数简单并解决这两个问题:

virtual ~Stack() {
    delete next;
}

free可以是

void free()
{
    delete next;
    next = nullptr;
}

(如果您要将结果丢弃,则无需进行大量的指针交换和元素复制。)