0

我已经实现了一个简单的链表,但是在删除我使用的所有指针后,它似乎并没有释放内存。我希望有人能指出我在这里缺少的东西。我正在使用活动监视器查看 xcode 的内存使用情况,也许这是查看内存使用情况的不准确方法?我不确定。这是代码:

#include <iostream>
struct node {
    int data;
    node *next;
};

//function to traverse linked list
void traverse(node *root){
    node *trav;

    std::cout << "The list is as follows:\n";
    //traverses through lists
    trav = root;
    do{
        std::cout << (*trav).data << "\n";
        trav = trav->next;
    }while(trav != NULL);
    std::cout << "\n";

    delete trav;
}

void addToList(node *root){
    node *tmp;
    tmp = root;
    if(tmp->next != NULL){
        while(tmp->next !=0){
            tmp = tmp->next;
        }
    }

    //adds new nodes to list
    for(int i = 0 ; i<1000000 ; i++){
        tmp->next = new node;
        tmp = tmp->next;
        tmp->data = i;  
    }
    tmp->next = NULL;

    delete tmp;
}

int main(int argc, const char * argv[])
{   
    node *root;         //this is the root node. it will not change

    //sets up root node
    root = new node;    //root points to a node structure (this is a memory address)
    root->next=NULL;       //next pointer is now "0"
    root->data = 5;     //data in this node = 5

    //print initial list
    traverse(root);
    //add to list
    addToList(root);
    //print new list
    traverse(root);

    delete root;
    return 0;
}

===================编辑 - 美国东部标准时间上午 9:28,1 月 27 日===================== ===========

感谢大家在这里的精彩反馈。我知道我做错了很多事情(这是我对 C++ 和动态内存分配的介绍。感谢您的帮助!)

请在下面找到更新的代码。我遇到了以下问题: 1 - 再次,活动监视器报告的实际内存使用情况没有从运行它的进程中释放(从终端运行和 xcode 都显示这个)。当发生以下故障时会发生这种情况,而且当我将 addToList 中的 for 循环设置为远低于 65514 时也会发生这种情况。--> 已修复 - 这不再是一个问题(缺少一对括号,导致我的代码无法正确删除。哎呀!)

2 -- 在 data = 65514 的节点,我得到: ~node 第一行的 EXC_BAD_ACCESS (code 2) -- 为什么?Xcode 提供以下内容:

Exception State Registers       
trapno  unsigned int    
err unsigned int    
faultvaddr  unsigned long   
Floating Point Registers        
fctrl   unsigned short  
fstat   unsigned short  
ftag    unsigned char   
fop unsigned short  
fioff   unsigned int    
fiseg   unsigned short  
fooff   unsigned int    
foseg   unsigned short  
mxcsr   unsigned int    
mxcsrmask   unsigned int    
stmm0   <invalid>   
stmm1   <invalid>   
stmm2   <invalid>   
stmm3   <invalid>   
stmm4   <invalid>   
stmm5   <invalid>   
stmm6   <invalid>   
stmm7   <invalid>   
xmm0    <invalid>   
xmm1    <invalid>   
xmm2    <invalid>   
xmm3    <invalid>   
xmm4    <invalid>   
xmm5    <invalid>   
xmm6    <invalid>   
xmm7    <invalid>   
xmm8    <invalid>   
xmm9    <invalid>   
xmm10   <invalid>   
xmm11   <invalid>   
xmm12   <invalid>   
xmm13   <invalid>   
xmm14   <invalid>   
xmm15   <invalid>   
General Purpose Registers       
rax unsigned long   
rbx unsigned long   0x0000000000000000
rcx unsigned long   
rdx unsigned long   
rdi unsigned long   
rsi unsigned long   
rbp unsigned long   0x00007fff6ca0c210
rsp unsigned long   0x00007fff6ca0c1c0
r8  unsigned long   
r9  unsigned long   
r10 unsigned long   
r11 unsigned long   
r12 unsigned long   0x0000000000000000
r13 unsigned long   0x0000000000000000
r14 unsigned long   0x0000000000000000
r15 unsigned long   0x0000000000000000
rip unsigned long   0x000000010d60cb96
rflags  unsigned long   
cs  unsigned long   
fs  unsigned long   
gs  unsigned long   

我认为这与无符号短路的限制有关,如在某些寄存器中所见,但是,我没有使用无符号短路。???!?!

这是我当前的代码:

#include <iostream>
struct node {
    long data;
    node *next;
    node():next(NULL){} //just to be safe, initialize as null to start
    //destructor used to clean up list nodes
    ~node(){
        std::cout << data << "  ";
        if(next != NULL){ 
            std::cout << data << ": deleted\n";
            delete next;
            next = NULL;
        }
    } 
};

//function to traverse linked list
void traverse(node *root){
    node *trav;

    std::cout << "The list is as follows:\n";
    //traverses through lists
    trav = root;
    do{
        std::cout << (*trav).data << "\n";
        trav = trav->next;
    }while(trav != NULL);
    std::cout << "\n";
}

void addToList(node *root){
    node *tmp;
    tmp = root;
    if(tmp->next != NULL){
        while(tmp->next !=0){
            tmp = tmp->next;
        }
    }

    //adds new nodes to list
    for(long i = 0 ; i<100000 ; i++){
        tmp->next = new node;
        tmp = tmp->next;
        tmp->data = i;  
    }
    tmp->next = NULL;    
}


int main(int argc, const char * argv[])
{   
    node *root;         //this is the root node. it will not change

    //sets up root node
    root = new node;    //root points to a node structure (this is a memory address)
    root->next=NULL;       //next pointer is now "0"
    root->data = 5;     //data in this node = 5

    //print initial list
    //traverse(root);
    //add to list
    addToList(root);
    //print new list
    //traverse(root);

    delete root;
    return 0;
}
4

4 回答 4

0

您不会删除您创建的所有节点,您必须遍历所有节点列表,从根节点开始,然后一个接一个地删除它们(应该考虑为此创建一个删除功能)

类似的东西:

node *actual, *next;
actual = root;

while (actual != null)
{
   next = actual->next;
   delete actual;
   actual = next;
}
于 2013-01-26T17:18:14.717 回答
0

问题是您删除了根节点,但这并不会自动删除直接和间接链接到它的所有节点。

如果您希望发生这种情况,您可以为指针指向的对象创建一个析构函数nodedelete如果nextthis 不是NULL。这将展开一系列析构函数调用,最终将删除链接到您要删除的节点的所有节点。

或者,如果您愿意,您甚至可以手动向后遍历列表和delete每个节点,但我不建议这样做 - 它不必要地复杂且容易出错。

另一个(不相关,但如果不是更糟的话同样糟糕)问题是在addToList(). 你不应该这样做:

delete tmp;

它将删除仍然由列表中的前一个节点指向的对象。这会创建一个悬空指针(即,似乎指向某个现有对象但实际上没有指向的非 NULL 指针)。

traverse()正如@us2012 正确指出的那样,类似的考虑适用于 function :

delete trav;

delete指令不会创建任何悬空指针,但没有用,因为当输入指针是(这是你的情况)delete时,操作员什么都不做。NULL

于 2013-01-26T17:19:31.073 回答
0

进行以下修改

struct node {
    int data;
    node *next;
    node():next(NULL){} //just to be safe
    ~node(){if(next != NULL) delete next;} 
}; 

问题是您只删除了根而不删除其他元素。此更改将确保当您删除一个节点时,它也会删除下一个节点(如果存在)。

不是最传统的解决方案,而是以自己的方式相当优雅..(如果列表太长,可能会导致堆栈问题,ppetrov 所示的 while 循环没有这个问题,还是尾递归?)

delete你还应该修复别人提到的其他多余的s。

这个实现接近于一个叫做Resource Acquisition Is Initialization的概念,这是一个帮助智能指针和库的其他部分工作的概念。

于 2013-01-26T17:19:41.263 回答
0

一般指南是,如果您的代码包含delete,那么您几乎可以肯定这样做是非常非常错误的。

struct node {
    int data;
    std::unique_ptr<node> next;
};

现在每个都nodenode在链中的下一个被销毁时销毁,这意味着列表会自行清理。您可以在堆栈上分配第一个node,允许它在超出范围时被释放,或者使用另一个unique_ptr.

您也绝对不想在遍历列表时删除它。

于 2013-01-26T17:35:35.147 回答