包含链接列表的链接列表会出现堆损坏错误:为什么?

时间:2012-10-30 22:17:49

标签: c++ list memory linked-list heap

长话短说:我制作了一个模板双链表,将列表放在其他列表中会导致问题。这个问题已经解决了。

我正在测试Double-Linked-Lists作为模板,我遇到了一个问题,将链表放在另一个链表中会出现以下错误:

(Microsoft Visual Studio 2010 Express)

  

Windows在linkListTesting.exe中触发了断点。这可能   是由于堆的损坏,这表明存在错误   linkListTesting.exe或它加载的任何DLL。这也可能是   由于用户在linkListTesting.exe具有焦点时按下F12。该   输出窗口可能有更多的诊断信息。

搜索此特定错误产生的结果是“确保您不使用未初始化的变量”,在这种情况下,当我将列表告诉push_back / push_front等时,它应该初始化列表我。 (不按F12,这样就可以了。)

这是我的主要cpp->

#include "vcLLMK2.h"

int main(int argc, char *argv[])
{
    vcList<vcList<int>> a;
    vcList<int> b;
    b.push_back(1);
    a.push_back(b);
    //ERROR HAPPENS HERE APPARENTLY
    return 0;
}

推回(或正面,也尝试过)将新条目(在本例中为'1')转换为'b'效果很好。没有崩溃,没有愚蠢。将'b'推入'a'会导致应用程序吐出上述错误,触发断点并死掉。

我最近开始使用SDL,并打算使用列表作为16层的容器,基本上是sprite / tiles / etc的列表。思维方式是程序将遍历包含图层的列表中的每个条目,然后渲染包含图层中的每个对象,然后再转到下一个。

“为什么不使用std :: list?” - 因为那是作弊

虽然很有可能那些在这个帖子中回答的人能够指出我的列表结构和实现中的每个明显的缺陷,我很幸福没有意识到,我最关心的是什么 - 特别是造成这种特殊错误以及如何解决它。也就是说,我对你们的问题是我构建的链接列表设置有什么问题导致上面的cpp在运行时出错?

以下链接列表结构是阅读以下页面的高潮:
http://www.codeproject.com/Articles/26631/Using-C-templates-for-functions-and-operators
http://www.cprogramming.com/tutorial/lesson15.html
http://geekswithblogs.net/MarkPearl/archive/2010/02/20/linked-lists-in-c.aspx
将其转换为模板主要是对此页面进行创造性解释的结果:
http://www.cplusplus.com/doc/tutorial/templates/

这是我的Link-List模板类,有很多评论 - &gt;

#pragma once
#ifndef vcLLMK2_H_INCLUDED
#define vcLLMK2_H_INCLUDED
#define NULL 0

//THIS IS ALL OUR NODE SHOULD EVER NEED
template <class T>
struct vcListNode
{
    public:
    T data;
    vcListNode<T> *next;
    vcListNode<T> *prev;
};

template <class T>
struct vcList
{
    public:
    vcListNode<T> *root;//the list's "start"
    vcListNode<T> *end;//the list's "end"
    vcListNode<T> *cur;//used to bounce around amongst entries

    int size();//returns number of non-null entries
    int count;//because keeping track of these is easier than iterating through every time

    void setAt(T,int);//sets data in a node
    void push_front(T);//inserts a new node at the beginning of the list
    void push_back(T);//same thing but back of list
    void removeAt(int);//kills a node at an arbitrary spot in the list
    void clear();//deletes all of the nodes in the list
    T getAt(int);//returns data from a node in the list
    vcList<T>& operator = (const vcList<T>&);//used for setting list a's contents to list b
    //EDIT COPYCONSTRUCTOR
    vcList<T>(const vcList<T>&);//copyconstructor

    vcList();// : root(NULL),end(root),cur(NULL),count(0){};
    ~vcList();
};

//constructor - sets everything to null and count to zero on init
template <class T>
vcList<T>::vcList()
{
    root=NULL;
    cur=NULL;
    end=NULL;
    count=0;
}

//destructor - deletes all nodes
template <class T>
vcList<T>::~vcList()
{
    clear();
}

//deletes all nodes from root to end
template <class T>
void vcList<T>::clear()
{
    if(root==NULL)//assume list has nothing in it, abort
    return;

    //set current targeted entry to root
    cur = root;
    //as long as we have somewhere to go...
    while(cur->next!=NULL)
    {
        //go there
        cur=cur->next;
        //and destroy where we were
        delete cur->prev;
    }
    //then destroy ourselves because nihilism is good for memory
    delete cur;
}

//used to set the contents of a list equal to those of another
template <class T>
vcList<T>& vcList<T>::operator= (const vcList<T> &fays)
{
    //for starters, clear ourselves of any unwanted garbagedata
    clear();

    //check the import list's root entry
    cur = fays.root;

    //if the list containing the values we are importing is empty, we're done, move along
    if(cur==NULL)
    return *this;

    //otherwise, make a new node - it's our new root
    vcListNode<T> *newEntry = new vcListNode<T>;
    newEntry->data = fays.root->data;
    newEntry->prev = NULL;
    newEntry->next = NULL;
    //set root/end to the new entry
    root = newEntry;
    end = newEntry;
    //update our count
    count=1;

    //fire through the import-list's entries
    //cur starts at root
    while(cur->next!=NULL)//(count<fays.count)
    {
        //move to next entry
        cur=cur->next;
        //add it to our list, push_back should update the location of 'end' for us
        push_back(cur->data);
    }
    //we should be done here, so go ahead and return everything
    return *this;
}

//this is mostly for convenience
template <class T>
int vcList<T>::size()
{
    return count;
}

//adds a new entry to the front of our linked list
template <class T> 
void vcList<T>::push_front(T info)
{
    //eat some memory
    vcListNode<T> *newEntry;
    newEntry = new vcListNode<T>;

    //set our memory and all that neat stuff
    newEntry->data = info;
    newEntry->next = root;
    newEntry->prev = NULL;

    //if our linked list is not empty
    if(root!=NULL)
        //set root's previous link to point at our new entry
        root->prev = newEntry;

    //if our linked list does not have an assigned end yet
    if(end==NULL)
        //assume it's empty and set end to our entry
        end=newEntry;

    //update the position of our root in the list, the beginning now begins at the beginning again
    root = newEntry;

    //and since we added something to the list, we should probably update our count of things in the list
    count++;
}

//this finds an element in the pointer-chain and sets its information
template <class T> 
void vcList<T>::setAt(T info,int index)
{
    //set our target to the root
    cur=root;

    //run through the list's entries until we're where we're supposed to be
    while(index>0)
    {
        cur=cur->next;
        index--;
    }

    //set the data in the cell/node/whatever to our input
    cur->data=info;
}

//returns the data contained in a node at a position in the list
template <class T> 
T vcList<T>::getAt(int meBro)
{
    //set target to root
    cur=root;
    //progress through the list
    while(meBro>0)
    {
        cur=cur->next;
        meBro--;
    }
    //dig the data out of the entry and return it
    return cur->data;
}

//adds an element-containing node to the end of the list-chain
template <class T> 
void vcList<T>::push_back(T info)
{
    //if our list already has entries, end shouldn't be null
    if(end!=NULL)
    //so just target our end slot
    cur=end;

    //if our list is empty, however
    else
        //target the root instead
        cur=root;

    //create our new node, put stuff in it, etc
    vcListNode<T> *newEntry;
    newEntry = new vcListNode<T>;
    newEntry->data = info;
    //we're adding to the END of the list so make next null
    newEntry->next = NULL;

    //if cur is NOT null, then theoretically we're pointed at the end of the list
    if(cur!=NULL)
        //set our new entry's previous pointer to the end
        newEntry->prev = cur;

    //cur IS null, which means the list is empty
    else
        //set our entry's previous pointer to be null, duh
        newEntry->prev = NULL;

    //if the end of our list exists
    if(end!=NULL)
    {
        //set the next entry in the list to point at our new entry
        end->next = newEntry;
        //then set end to target the new entry
        end=newEntry;
    }
    //and if our list does not have an end yet for some reason (read as: it's empty)
    else
    {
        //set the root to our new entry
        root = newEntry;
        //set the end to our new entry as well, since there's only one entry in the list
        end = newEntry;
    }

    //update count of number of objects in list
    count++;
}

//this deletes/removes/destroys/obliterates a node at a location in the list
template <class T> 
void vcList<T>::removeAt(int index)
{
    //for starters - is what we're trying to kill even here?
    if(index>=count)
        //NOPE GET OUT
        return;

    //later on it might speed things up to check whether the distance from end or root is shorter
    //for now, just start at the beginning

    //target the root
    cur=root;

    //move through the list to the specified entry
    while(index>0)
    {
        index--;
        cur=cur->next;
    }

    //if the previous entry exists
    if(cur->prev!=NULL)
        //point its next at the entry after this one
        cur->prev->next=cur->next;

    //if the previous entry is NULL, it means we're at the root
    //so tell root to scoot forward one entry
    //if there's a forward to scoot to
    else if(cur->next != NULL)
    root = cur->next;

    //if the next entry exists
    if(cur->next!=NULL)
        //set the next entry's previous pointer to point at the entry before the targeted one
        cur->next->prev=cur->prev;

    //if the next entry does not exist, we must be at the end of the list
    //so tell the end of the list to scoot back one slot
    //if there's a back-one-slot to go to
    else if(cur->prev!=NULL)
    end = cur->prev;

    //remove the entry at the targeted location
    delete cur;

    //decrement our count
    count--;
}
//EDIT -> Copy Constructor
//copy constructor, similar as suggested to the operator=
template <class T>
vcList<T>::vcList(const vcList<T>& fays)
{
    //might not be completely necessary, but we're not hurting anything by making sure
    clear();

//check the import list's root entry
cur = fays.root;

    //if the list containing the values we are importing is empty, we're done, move along
    if(cur==NULL)
    return;//just return, constructors don't get return types

    //otherwise, make a new node - it's our new root
    vcListNode<T> *newEntry = new vcListNode<T>;
    newEntry->data = fays.root->data;
    newEntry->prev = NULL;
    newEntry->next = NULL;
    //set root/end to the new entry
    root = newEntry;
    end = newEntry;
    //update our count
    count=1;

    //fire through the import-list's entries
    //cur starts at root
    while(cur->next!=NULL)//(count<fays.count)
    {
        //move to next entry
        cur=cur->next;
        //add it to our list, push_back should update the location of 'end' for us
        push_back(cur->data);
    }
    //we should be done here, so go ahead and return everything
    //return *this;
}
#endif

1 个答案:

答案 0 :(得分:1)

我认为你应该看看C ++中最重要的一个概念,即通过引用传递。 push_back方法作为名为info的参数,即用于调用方法的对象的副本。由于您没有明确告诉编译器如何创建vsList对象的副本,因此它将创建一个默认的复制构造函数,但它不能按预期工作。

因此,当调用存储在对象“a”中的对象的析构函数时,代码上的问题确实会发生。由于它没有有效的指针,它会崩溃。您可以通过两种方式解决您的问题,更改push_back(以及所有其他方法)以通过“const reference”而不是通过值或实现复制构造函数来接收T.