我的代码中是否存在内存泄漏?
Do I have a memory leak in my code?
我在 IntList
实现 push_back
节点中有一小段代码。我的问题是 *pnode
是否会导致内存泄漏,或者我是否需要在最后删除它。
void IntList::push_back(int data){
if (first){
IntNode *pNode = first;
while(pNode->next!=0){pNode = pNode->next;}
pNode->next = new IntNode(data);
} else
first = new IntNode(data);
}
不,您不需要在 pNode
上致电 delete
。您只能对使用 new
创建的事物调用 delete
。使用现在的代码 pNode
是一个堆栈对象,当它在函数结束时超出范围时将自动销毁。
您最终必须删除使用 new 创建的 IntNode,可能在容器的析构函数和 pop_back 函数中。 pNode本身(指针)是在栈上分配的,并没有new,所以不需要删除。
您不需要删除 pNode
。此外,在这种特殊情况下你不能这样做。
使用 new
创建内容后,您必须 delete
只使用一次 - 一旦您永远不会使用它。
使用 delete
删除对象后,尝试读取其内容是一个 undefined behavior. The pointers are generally one of the most bug generating part of the c++,因此您尝试更好地理解它是件好事。
你可以这样想象自己:你可以用new
买房子(房子是一段记忆)。它returns 你的地址好象呢。它现在是您的房子,您可以随心所欲地使用它。您也可以用 delete
出售它。在此之前,您可以将您的家庭住址告诉您的朋友,这样他们就可以来找您。地址的分配,导致复制它(例如 IntNode *pNode = first;
)。但是,您仍然只有一所房子。所以无论你复制多少次你的家庭地址,你只能卖一次房子。
我建议使用智能指针(例如std::unique_ptr
),但我认为这个程序是为了学习编程,所以不要这样做;)
您需要在从列表中删除节点的每个函数上删除节点,而不是在插入时。不这样做会导致泄漏。
如果在销毁对象时仍有分配的节点,则需要遍历整个列表以删除所有节点。不这样做也会导致泄漏。
我想这是一项大学作业,因为那里有数百万经过实战检验的链表实现。
如果您在所有函数上都保持一个尾节点是最新的,您会做得更好,这样您就可以避免迭代整个列表以在尾部插入一个节点。
您显示的代码不足以判断您是否正在泄漏内存。您显示了负责分配的例程,但没有显示您执行释放的代码。
如果您没有任何执行重新分配的代码,那么是的,显然是泄漏。 C++ 不执行自动垃圾收集,就像您在其他一些语言中可能习惯的那样。
您正在使用裸 new
,因此即使您确实有一些其他代码试图进行重新分配,也有一个很好的改变,它被错误地完成了。
在 C++ 中,您通常不应直接使用 new
运算符,而应该学习使用 RAII 来处理资源。 IntList 大概使用拥有原始指针,这是另一件要避免的事情。在这种情况下,您可能应该使用 unique_ptr<IntNode>
。例如:
struct IntList {
struct IntNode {
unique_ptr<IntNode> next;
int data;
IntNode(int data) : data(data) {}
};
unique_ptr<IntNode> first;
// returns a reference to the null pointer which terminates the linked list
unique_ptr<IntNode> &get_tail() {
if (!first) {
return first;
}
IntNode *pNode = first.get(); // pNode is a non-owning pointer
while (pNode->next) {
pNode = pNode->next.get();
}
return pNode->next;
}
void push_back(int data) {
get_tail() = make_unique<IntNode>(data);
}
};
上面的代码确实有问题,但与内存泄漏无关。当列表销毁时,first
自动销毁,first->next
自动销毁,依此类推。有可能将如此多的元素插入到列表中,以至于此破坏链 'smashes' 函数堆栈,导致未定义的行为。
可以使用以相反顺序销毁节点的 IntList 析构函数来解决此问题:
IntList::~IntList() {
while (first) {
unique_ptr<IntNode> tmp = std::move(first);
first = std::move(tmp->next);
}
}
有趣的是,这是 Rule of Three 的一个例外示例。
我在 IntList
实现 push_back
节点中有一小段代码。我的问题是 *pnode
是否会导致内存泄漏,或者我是否需要在最后删除它。
void IntList::push_back(int data){
if (first){
IntNode *pNode = first;
while(pNode->next!=0){pNode = pNode->next;}
pNode->next = new IntNode(data);
} else
first = new IntNode(data);
}
不,您不需要在 pNode
上致电 delete
。您只能对使用 new
创建的事物调用 delete
。使用现在的代码 pNode
是一个堆栈对象,当它在函数结束时超出范围时将自动销毁。
您最终必须删除使用 new 创建的 IntNode,可能在容器的析构函数和 pop_back 函数中。 pNode本身(指针)是在栈上分配的,并没有new,所以不需要删除。
您不需要删除 pNode
。此外,在这种特殊情况下你不能这样做。
使用 new
创建内容后,您必须 delete
只使用一次 - 一旦您永远不会使用它。
使用 delete
删除对象后,尝试读取其内容是一个 undefined behavior. The pointers are generally one of the most bug generating part of the c++,因此您尝试更好地理解它是件好事。
你可以这样想象自己:你可以用new
买房子(房子是一段记忆)。它returns 你的地址好象呢。它现在是您的房子,您可以随心所欲地使用它。您也可以用 delete
出售它。在此之前,您可以将您的家庭住址告诉您的朋友,这样他们就可以来找您。地址的分配,导致复制它(例如 IntNode *pNode = first;
)。但是,您仍然只有一所房子。所以无论你复制多少次你的家庭地址,你只能卖一次房子。
我建议使用智能指针(例如std::unique_ptr
),但我认为这个程序是为了学习编程,所以不要这样做;)
您需要在从列表中删除节点的每个函数上删除节点,而不是在插入时。不这样做会导致泄漏。
如果在销毁对象时仍有分配的节点,则需要遍历整个列表以删除所有节点。不这样做也会导致泄漏。
我想这是一项大学作业,因为那里有数百万经过实战检验的链表实现。
如果您在所有函数上都保持一个尾节点是最新的,您会做得更好,这样您就可以避免迭代整个列表以在尾部插入一个节点。
您显示的代码不足以判断您是否正在泄漏内存。您显示了负责分配的例程,但没有显示您执行释放的代码。
如果您没有任何执行重新分配的代码,那么是的,显然是泄漏。 C++ 不执行自动垃圾收集,就像您在其他一些语言中可能习惯的那样。
您正在使用裸 new
,因此即使您确实有一些其他代码试图进行重新分配,也有一个很好的改变,它被错误地完成了。
在 C++ 中,您通常不应直接使用 new
运算符,而应该学习使用 RAII 来处理资源。 IntList 大概使用拥有原始指针,这是另一件要避免的事情。在这种情况下,您可能应该使用 unique_ptr<IntNode>
。例如:
struct IntList {
struct IntNode {
unique_ptr<IntNode> next;
int data;
IntNode(int data) : data(data) {}
};
unique_ptr<IntNode> first;
// returns a reference to the null pointer which terminates the linked list
unique_ptr<IntNode> &get_tail() {
if (!first) {
return first;
}
IntNode *pNode = first.get(); // pNode is a non-owning pointer
while (pNode->next) {
pNode = pNode->next.get();
}
return pNode->next;
}
void push_back(int data) {
get_tail() = make_unique<IntNode>(data);
}
};
上面的代码确实有问题,但与内存泄漏无关。当列表销毁时,first
自动销毁,first->next
自动销毁,依此类推。有可能将如此多的元素插入到列表中,以至于此破坏链 'smashes' 函数堆栈,导致未定义的行为。
可以使用以相反顺序销毁节点的 IntList 析构函数来解决此问题:
IntList::~IntList() {
while (first) {
unique_ptr<IntNode> tmp = std::move(first);
first = std::move(tmp->next);
}
}
有趣的是,这是 Rule of Three 的一个例外示例。