我使用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();
}
这个程序运行正常。
两者有什么区别?
答案 0 :(得分:1)
当你在tmp
中删除pop
时,你会调用Stack
的析构函数,在你的第一个代码中加倍free
:
以下是推送1和2后的堆栈状态(p1
和p2
是Stack
中创建的新push
个对象:
| data | next
-------------------------------
s | uninitialized | p1
p1 | 1 | p2
p2 | 2 | nullptr
现在,如果您在第一段代码中销毁s
(忽略data
会发生什么):
s.free
已执行。s.next
为p1
,因此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->next
为p2
)。
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;
}
(如果您要将结果丢弃,则无需进行大量的指针交换和元素复制。)