两个程序的区别?
Difference between two program?
我使用列表节点创建了一个堆栈 class。
但是一个工作正常,另一个总是崩溃。
两个程序有什么区别?
我认为析构函数会破坏
#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();
}
这个程序工作正常。
两者有什么区别?
当您在 pop
中删除 tmp
时,您会调用 Stack
的析构函数,在您的第一个代码中它会加倍 free
:
这是推送 1 和 2 后堆栈的状态(p1
和 p2
是在 push
中创建的新 Stack
对象):
| 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
成为悬挂指针,所以代码 "works fine".
你应该这样修改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)
。
首先,您的 pop
已损坏 - delete tmp;
递归删除以 tmp
开头的整个子堆栈,而不仅仅是顶部节点。
(tmp
的析构函数也会 free();
,依此类推。)
这会导致双重删除。
您的第二个版本会泄漏内存,除非您记得在完成堆栈后调用 free()
。
您可以保持析构函数简单并解决这两个问题:
virtual ~Stack() {
delete next;
}
和free
可以是
void free()
{
delete next;
next = nullptr;
}
(如果您要丢弃结果,则无需进行大量指针交换和元素复制。)
我使用列表节点创建了一个堆栈 class。 但是一个工作正常,另一个总是崩溃。 两个程序有什么区别? 我认为析构函数会破坏
#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();
}
这个程序工作正常。
两者有什么区别?
当您在 pop
中删除 tmp
时,您会调用 Stack
的析构函数,在您的第一个代码中它会加倍 free
:
这是推送 1 和 2 后堆栈的状态(p1
和 p2
是在 push
中创建的新 Stack
对象):
| 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
成为悬挂指针,所以代码 "works fine".
你应该这样修改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)
。
首先,您的 pop
已损坏 - delete tmp;
递归删除以 tmp
开头的整个子堆栈,而不仅仅是顶部节点。
(tmp
的析构函数也会 free();
,依此类推。)
这会导致双重删除。
您的第二个版本会泄漏内存,除非您记得在完成堆栈后调用 free()
。
您可以保持析构函数简单并解决这两个问题:
virtual ~Stack() {
delete next;
}
和free
可以是
void free()
{
delete next;
next = nullptr;
}
(如果您要丢弃结果,则无需进行大量指针交换和元素复制。)