1

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

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

(微软 Visual Studio 2010 速成版)

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 层的容器,这些层基本上是要渲染的精灵/瓷砖/等列表。思维模式是程序将遍历包含层列表中的每个条目,然后渲染包含层中的每个对象,然后再继续下一个。

“为什么不直接使用 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/

这是我的链接列表模板类,有很多评论->

#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
4

1 回答 1

1

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

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

于 2012-10-30T23:17:04.080 回答