析构函数怎么析构一条链表

时间:2022-01-04 19:35:15
我在仿照《提高C++性能的编程技术》里的例子写一个一个固定大小对象的内存池,

我用vs2015运行,会在析构函数那里报错:
“引发了异常: 读取访问权限冲突。”

下面是我把出问题的地方提出来重写的
class Test
{
public:
Test() :next(NULL) { a++; }
~Test()
{
Test *i = next;
while (i)
{
next = i->next;
delete i;
i = next;
}
}
void alloc(size_t num)
{
Test *head = new Test;
next = head;
for (int i = 0; i < num - 1; ++i)
{
head->next = new Test;
head = head->next;
}
head = NULL;
}

Test* next;
static int a;
};

int Test::a = 0;

int main()
{
Test *t = new Test;
t->alloc(5);

delete t; // err

return 0;
}

我看见网上有类似的代码:
一个高效的内存池实现 - 厚积薄发 - C++博客 http://www.cppblog.com/weiym/archive/2012/05/05/173785.html

13 个解决方案

#1


程序是有问题的,
删除内存的时候,应该从链表尾巴开始 一个个删除。

你现在的做法,删除第一个test的时候,会试图删除整个链表,
但是删除到第二个节点的时候,他的析构函数会把第二个节点以及之后的链表都删除掉了,
即,一个节点会被删除多次。因此,出问题。

而且,现在这么设计怪怪的,但是怎么设计更好一时也说不出来的。

#2


这样递归了, 直接终止就行, 
 ~Test()
    {
        if(next)
            delete next;
   }

#3


引用 1 楼 relaxisland 的回复:
程序是有问题的,
删除内存的时候,应该从链表尾巴开始 一个个删除。

你现在的做法,删除第一个test的时候,会试图删除整个链表,
但是删除到第二个节点的时候,他的析构函数会把第二个节点以及之后的链表都删除掉了,
即,一个节点会被删除多次。因此,出问题。

而且,现在这么设计怪怪的,但是怎么设计更好一时也说不出来的。


我开始也是这个感觉:从表头开始析构不会重复析构吗?但是我设置了if()判断后还是有这个问题。但是书上这么写的,我也没有经验,所以接下去不知道怎么办了。

#4


引用 2 楼 fly_dragon_fly 的回复:
这样递归了, 直接终止就行, 
 ~Test()
    {
        if(next)
            delete next;
   }


好像可以,能帮我看看源码吗?vs不怎么会用,vs给的信息很多,但是太多了,看不出问题。 就看下有没有内存泄露就行

#5


崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。

#6


#include <iostream>
using namespace std;

class Test
{
public:
    Test() :next(NULL)
    {
        a++;
    }
    
    ~Test()
    {
        Test *i = next;
        while (i)
        {
            Test *temp = i->next;
            delete temp;
            temp = NULL;
            i = next;
        }
    }
    
    void alloc(size_t num)
    {
        Test *head = new Test;
        next = head;
        for (int i = 0; i < num - 1; ++i)
        {
            head->next = new Test;
            head = head->next;
        }
        head = NULL;
    }
    
    Test* next;
    static int a;
};

int Test::a = 0;

int main()
{
    Test *t = new Test;
    t->alloc(5);
    
    delete t; // err
    
    return 0;
}

#7


引用 6 楼 u013965885 的回复:
#include <iostream>
using namespace std;

class Test
{
public:
    Test() :next(NULL)
    {
        a++;
    }
    
    ~Test()
    {
        Test *i = next;
        while (i)
        {
            Test *temp = i->next;
            delete temp;
            temp = NULL;
            i = next;
        }
    }
    
    void alloc(size_t num)
    {
        Test *head = new Test;
        next = head;
        for (int i = 0; i < num - 1; ++i)
        {
            head->next = new Test;
            head = head->next;
        }
        head = NULL;
    }
    
    Test* next;
    static int a;
};

int Test::a = 0;

int main()
{
    Test *t = new Test;
    t->alloc(5);
    
    delete t; // err
    
    return 0;
}


不行,

#8


#include "stdafx.h"
#include <stdlib.h>
#include<stdio.h>
#include<string>
#include <iostream>
struct Node
{
Node* next;
 int a;
};
class Test
{
public:
Test() :Head(NULL) {  }
~Test()
{
Node *i = Head;
while (i)
{
Head = i->next;
delete i;
i = Head;
}
}
void alloc(size_t num)
{
Head = new Node;
Node* P = Head;

for (int i = 0; i < num - 1; ++i)
{
P->next = new Node;
P = P->next;
}
P->next = NULL;
}

Node* Head;
};

int _tmain(int argc, _TCHAR* argv[])
{
Test *t = new Test;
t->alloc(5);

delete t; // err
return 0;
}


你这个代码写的实在是有问题啊
每个对象会调用析构函数
你这样会重复释放Test 
也就是Test 会被释放多次~~~
你能不报内存错误不?

#9


t->alloc(5);
你把你的参数才成0 或者1 你就知道为什么会这样呢~~~~

#10


你可以参考一下 链表~~~数据结构~~~
哥们~~~

#11


假设你对象分别为A B C D E 四个对象, 当你析构第一个对象A时, A的析构函数里尝试在while里依次delete B C D E, delete操作又会调用 B C D E的析构函数, 那么在 B C D E 里同样会调用while循环,造成重复删除,并且你删除的时候存在野指针,同样触发异常  

#12


在test类内调用alloc函数分配内存,该内存存储的是test类型变量,析构的时候每个test对象都会调用析构函数来释放该这个next链表,最后会重复释放造成程序异常退出。
建议你在释放时加一条if(i==this)来判断释放可以释放

#13


谢谢楼上的帮助,

果然还是 delete 这出了问题,可能是作者使用的编译器更高级一点吧,会自动辨别内存类型,
因为中间开辟内存的时候是根据大小开的,用的是 (T*)new char[size],我的例子里没体现出来,
最后把析构改成这样,看上去是解决问题了:

~Test()
{
/*if (next)
delete next;*/
Test *i = next;
while (i)
{
next = i->next;
//delete[] (char*)(i);
delete[] reinterpret_cast<char*>(i);
i = next;
}
}

#1


程序是有问题的,
删除内存的时候,应该从链表尾巴开始 一个个删除。

你现在的做法,删除第一个test的时候,会试图删除整个链表,
但是删除到第二个节点的时候,他的析构函数会把第二个节点以及之后的链表都删除掉了,
即,一个节点会被删除多次。因此,出问题。

而且,现在这么设计怪怪的,但是怎么设计更好一时也说不出来的。

#2


这样递归了, 直接终止就行, 
 ~Test()
    {
        if(next)
            delete next;
   }

#3


引用 1 楼 relaxisland 的回复:
程序是有问题的,
删除内存的时候,应该从链表尾巴开始 一个个删除。

你现在的做法,删除第一个test的时候,会试图删除整个链表,
但是删除到第二个节点的时候,他的析构函数会把第二个节点以及之后的链表都删除掉了,
即,一个节点会被删除多次。因此,出问题。

而且,现在这么设计怪怪的,但是怎么设计更好一时也说不出来的。


我开始也是这个感觉:从表头开始析构不会重复析构吗?但是我设置了if()判断后还是有这个问题。但是书上这么写的,我也没有经验,所以接下去不知道怎么办了。

#4


引用 2 楼 fly_dragon_fly 的回复:
这样递归了, 直接终止就行, 
 ~Test()
    {
        if(next)
            delete next;
   }


好像可以,能帮我看看源码吗?vs不怎么会用,vs给的信息很多,但是太多了,看不出问题。 就看下有没有内存泄露就行

#5


崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。

#6


#include <iostream>
using namespace std;

class Test
{
public:
    Test() :next(NULL)
    {
        a++;
    }
    
    ~Test()
    {
        Test *i = next;
        while (i)
        {
            Test *temp = i->next;
            delete temp;
            temp = NULL;
            i = next;
        }
    }
    
    void alloc(size_t num)
    {
        Test *head = new Test;
        next = head;
        for (int i = 0; i < num - 1; ++i)
        {
            head->next = new Test;
            head = head->next;
        }
        head = NULL;
    }
    
    Test* next;
    static int a;
};

int Test::a = 0;

int main()
{
    Test *t = new Test;
    t->alloc(5);
    
    delete t; // err
    
    return 0;
}

#7


引用 6 楼 u013965885 的回复:
#include <iostream>
using namespace std;

class Test
{
public:
    Test() :next(NULL)
    {
        a++;
    }
    
    ~Test()
    {
        Test *i = next;
        while (i)
        {
            Test *temp = i->next;
            delete temp;
            temp = NULL;
            i = next;
        }
    }
    
    void alloc(size_t num)
    {
        Test *head = new Test;
        next = head;
        for (int i = 0; i < num - 1; ++i)
        {
            head->next = new Test;
            head = head->next;
        }
        head = NULL;
    }
    
    Test* next;
    static int a;
};

int Test::a = 0;

int main()
{
    Test *t = new Test;
    t->alloc(5);
    
    delete t; // err
    
    return 0;
}


不行,

#8


#include "stdafx.h"
#include <stdlib.h>
#include<stdio.h>
#include<string>
#include <iostream>
struct Node
{
Node* next;
 int a;
};
class Test
{
public:
Test() :Head(NULL) {  }
~Test()
{
Node *i = Head;
while (i)
{
Head = i->next;
delete i;
i = Head;
}
}
void alloc(size_t num)
{
Head = new Node;
Node* P = Head;

for (int i = 0; i < num - 1; ++i)
{
P->next = new Node;
P = P->next;
}
P->next = NULL;
}

Node* Head;
};

int _tmain(int argc, _TCHAR* argv[])
{
Test *t = new Test;
t->alloc(5);

delete t; // err
return 0;
}


你这个代码写的实在是有问题啊
每个对象会调用析构函数
你这样会重复释放Test 
也就是Test 会被释放多次~~~
你能不报内存错误不?

#9


t->alloc(5);
你把你的参数才成0 或者1 你就知道为什么会这样呢~~~~

#10


你可以参考一下 链表~~~数据结构~~~
哥们~~~

#11


假设你对象分别为A B C D E 四个对象, 当你析构第一个对象A时, A的析构函数里尝试在while里依次delete B C D E, delete操作又会调用 B C D E的析构函数, 那么在 B C D E 里同样会调用while循环,造成重复删除,并且你删除的时候存在野指针,同样触发异常  

#12


在test类内调用alloc函数分配内存,该内存存储的是test类型变量,析构的时候每个test对象都会调用析构函数来释放该这个next链表,最后会重复释放造成程序异常退出。
建议你在释放时加一条if(i==this)来判断释放可以释放

#13


谢谢楼上的帮助,

果然还是 delete 这出了问题,可能是作者使用的编译器更高级一点吧,会自动辨别内存类型,
因为中间开辟内存的时候是根据大小开的,用的是 (T*)new char[size],我的例子里没体现出来,
最后把析构改成这样,看上去是解决问题了:

~Test()
{
/*if (next)
delete next;*/
Test *i = next;
while (i)
{
next = i->next;
//delete[] (char*)(i);
delete[] reinterpret_cast<char*>(i);
i = next;
}
}