堆腐败,可能的内存泄漏,C + +

时间:2009-09-19 19:38:10

标签: c++

我有一份我差不多完成的家庭作业。

由于效率低下,我只是想知道如何在程序结束时防止崩溃。

quack::quack(int capacity) : backPtr( NULL ), frontPtr( NULL )
{
    items    = new item[capacity];
    backPtr  = new item;
    frontPtr = new item;
    midPtr   = new item;
    current  = new item;

    maxSize = capacity;
    back    = maxSize-1;
    count   =  0;
    top     = -1;
}

quack::~quack(void)
{
    delete frontPtr;
    delete backPtr;
    delete current;
    delete midPtr;
    delete [] items; //Heap Corruption Debug Error at the end of program.

    items    = NULL;
    maxSize  =  0;
    back     =  0;
}

bool quack::pushFront(const int n)
{       
    int i = 0;

    if ( count == maxSize ) // Then we cant add to it n e more. 
    {   
        throw runtime_error( "Stack is Full" );// Full Stack
        return false;
    }
        backPtr->n = items[back-1].n;
        while ( i < count ) // Loop less than however many we counted.
        {
            if ( i == top+1 )
            {
                current->n = items[top+1].n;   
                items[top+1].n = backPtr->n;
            }

            midPtr->n  = items[++i].n;
            items[i].n = current->n;

            if ( i != back-1 )
            {
                current->n = items[++i].n;
                items[i].n = midPtr->n;
            }
        }
        ++count;
        items[top+1].n = n;
        return true;    
}

bool quack::pushBack(const int n)
{   
    items[count].n = n;
    count++;
    return true;
}

bool quack::popFront(int& n)
{
    n = items[top+1].n;
    for ( int i = 0; i < count; i++ )
    {
        items[i] = items[i+1];
    }
    count--;   // Remove top element.
    return true;
}

bool quack::popBack(int& n)
{

    n = items[--count].n;
    return true;
}

void quack::rotate(int r)
{
    int i = 0;

    while ( r > 0 ) // rotate postively.
    {
        frontPtr->n = items[top+1].n;
        for ( int i = 0; i < back; i++ )
        {
            items[i] = items[i+1];                  
        }
        items[back-1].n = frontPtr->n;
        r--;
    }

    while ( r < 0 )  // rotate negatively.
    {
        if ( i == top+1 )
        {
            backPtr->n = items[back-1].n;
            current->n = items[top+1].n;  
            items[top+1].n = backPtr->n;
        }
            midPtr->n  = items[++i].n;
            items[i].n = current->n;

            if ( i == back-1 )
            {
                items[back-1].n = current->n;
                i = 0;
                r++; continue;
            }
        else
        {
            current->n = items[++i].n;
            items[i].n = midPtr->n;
            if ( i == back-1 )
            {
                i = 0;
                r++; continue;
            }
        }
    }
}

void quack::reverse(void)
{
    int j = 0; // Variable declaration/initialization.

    frontPtr->n = items[top+1].n;
    backPtr->n  = items[back-1].n;

    for ( int i = 0; i < count / 2; i++ )
    {
        items[j].n = items[i].n;
        items[i].n = items[ count - i-1 ].n;
        items[ count - i-1 ].n = items->n;
    }

    items[top+1].n = backPtr->n;
    items[back-1].n = frontPtr->n;
}

int quack::itemCount(void)
{
    return count;
}

ostream& operator<<(ostream& out, quack& q)
{
    if ( q.count == 0 ) // No elements have been counted.
    out << endl << "quack: empty" << endl;
    else
    {
        out << endl << "quack: ";
        for ( int i = 0; i < q.count; i++ )
        {
            if ( i < q.count-1 )
                out << q.items[i].n << ", ";
            else out << q.items[i].n;
        }
        out << endl << endl;
    }
    return out;
}

和头文件:

#include <ostream>

using namespace std;

class quack
{
public:
    quack(int capacity);
    ~quack(void);
    bool pushFront(const int n);    // Push an item onto the front.
    bool pushBack(const int n);     // Push an item onto the back.
    bool popFront(int& n);          // Pop an item off the front.
    bool popBack(int& n);           // Pop an item off the back.
    void rotate(int r);             // "rotate" the stored items (see note below).
    void reverse(void);             // Reverse the order of the stored items.
    int itemCount(void);            // Return the current number of stored items.

private:
    int maxSize; // is for the size of the item stack
    int back;    // is for the back or "bottom" of the stack
    int count;   // to count the items added to the stack
    int top;

    struct item                     // Definition of each item stored by the quack.
    {
        int     n;
    };

    item        *items;             // Pointer to storage for the circular array.
    item        *backPtr;
    item        *frontPtr;
    item        *midPtr;
    item        *current;

public:
    friend ostream& operator<<(ostream& out, quack& q);
};

5 个答案:

答案 0 :(得分:5)

在阅读代码时,我将对此进行几次编辑,请原谅我。看起来你正在实现一个双端队列(出队)。

构造

items    = new item[capacity];
backPtr  = new item;
frontPtr = new item;
midPtr   = new item;
current  = new item;

这没有意义。您的前/后/中/当前指针实际上并不指向您的某个项目。您可能希望frontPtr = items+0backPtr = items + capacity-1(反之亦然)。不确定dePue需要midPtr或current for。

[编辑:似乎项目是struct item { int n },你只是在复制n。你有一个回溯指数和最高指数...]

析构

delete frontPtr;
delete backPtr;
delete current;
delete midPtr;
delete [] items; //Heap Corruption Debug Error at the end of program.

自前/后/等。应该指向项目内部,你可以将其中一些项目双重释放。这可能是你的堆腐败崩溃。 [编辑:或不,考虑到奇怪的复制]

items        = NULL;
maxSize  =  0;
back         =  0;

这看起来相当愚蠢(对象即将不再;谁在乎?)......

错误,

好的,简单的dequeue正常工作方式是拥有一个元素数组:

items      ->  1   2   3   4   5   6   7   8   9   …
front_ptr  -----------/                   /|\
back_ptr   --------------------------------+

然后你有一个指针(frontPtr)指向数组中第一个使用的点和另一个指向最后一个使用点的指针(backPtr)。所以,pop_front会做这样的事情:

if (frontPtr <= backPtr) {
  return *frontPtr++
} else {
  // tried to pop from empty dequeue, handle error
}

那将返回3,并将front_ptr提前指向4. pop_back将类似(但是测试被反转,并且 - 而不是++)。

或者,您可以存储索引而不是指针。但选择一个,不要同时使用索引和指针。

答案 1 :(得分:2)

指针是需要一段时间才能习惯的事情之一。

分配时

items    = new item[capacity]

你实际上有一个指针'items'指向堆上分配的'item'数组的第一个元素:

items -> {item}{item}{item}..{item}  // capacity

您的其他指针也应该指向同一个数组,以后不应该用它来删除它们指向的内容,而只是删除'items':

delete [] items

数组和指针是可以互换的。

项目+ 1与&amp; items [1]相同 *(项目+ 1)与项目[1]相同。

这使“当前”指向数组中的第二项:

current = items + 1 

这不起作用,因为'current'是指针,而items [1]是数组中的item对象,而不是地址:

current = items[1]

但这有效:

current = &items[1] 

答案 2 :(得分:1)

您的主要问题是要了解backptr,frontptr,midptr和current与项目的目的不同。从技术上讲,它们都指向一些内存位置,但语义上的项目是数据容器(数组)的锚点,而其他指针的目的是管理数据(簿记)。

因此,item是你应该为你分配的内存分配的唯一指针。其他指针应该指向数组(列表)项目点。

因此,你应该在c'tor和d'tor中将backptr,frontptr,midptr和current设置为NULL,并且只使用new和delete with items。

顺便说一下,我不知道你的任务是否允许它,但是使用链表而不是数组可能会让你在管理列表方面更轻松(就像使用int类型的backptr,frontptr,midptr和current一样)。

答案 3 :(得分:1)

删除items数组时崩溃的两个可能原因。

  1. 该类没有自定义复制构造函数或赋值运算符。这是必不可少的,因为内置的将是不好的。如果您通过值quack作为参数传递,或者从函数返回一个,或将quack复制到变量中,您将有两个quack指向同一个{ {1}}数组。当第二个破坏时,它将第二次尝试items数组,正如John Lennon在他着名的关于删除堆对象的歌曲中观察到的那样,“不,不,不是第二次”。

  2. 您在代码中对delete数组进行了大量编写。如果你在数组的末尾(或者在开始之前)写入,那么当你删除它时会检测到,因为在那时堆实现会发现你已经删除了释放内存块所需的一些管理信息。 。当然,大多数人在开始尝试执行此操作时,您的代码中肯定存在错误。

  3. 在C ++中有一种简单的方法来检测它 - 不要使用原始数组。使用包装数组的类,并对每次访问执行边界检查。您可以使用items并使用std::vector函数代替at运算符(未进行边界检查)来获取此值。

答案 4 :(得分:0)

使用valgrind运行它。