2009-08-26 157 views
2

我必须使用C++中的数组编写一个堆栈类模板以供我分配。在析构函数中释放导致内存泄漏

Here是我的代码:

#include <iostream> 

template <typename Type> 
class Stack { 
private: 
    int top; 
    Type items[]; 
public: 
    Stack() { top = -1; }; 
    ~Stack() { delete[] items; }; 
    void push(Type); 
    bool isEmpty() const; 
    Type pop(); 
    Type peek() const; 
}; 

int main (int argc, char *argv[]) { 
    Stack<double> st; 
    return 0; 
} 

template<typename Type> 
void Stack<Type>::push(Type item) { 
    top++; 
    if(top == sizeof(items)/sizeof(Type)) { 
    Type buff[] = new Type[top]; 
    std::copy(items,items+top,buff); 
    delete[] items; 
    items = new Type[2*top]; 
    std::copy(buff,buff+top,items); 
    delete[] buff; 
    } 
    items[top] = item; 
} 

template<typename Type> 
bool Stack<Type>::isEmpty() const { 
    return top == -1 ? true : false; 
} 

template<typename Type> 
Type Stack<Type>::pop() { 
    //TODO 
    return items[top--]; 
} 

template<typename Type> 
Type Stack<Type>::peek() const{ 
    //TODO 
    return items[top-1]; 
} 

它使用 “g++ -Wall” 编译正常,但是当我运行该程序,我得到这个错误:

* glibc detected *./lab3: munmap_chunk(): invalid pointer: 0x00007fff41a3cdf8

尝试了一下使用GDB后,我发现错误发生在该行:

'free[] items' in the destructor. 

I不明白为什么释放数组会导致内存泄漏?任何线索?

+1

下一次你可以请有代码在stackoverflow,所以当有人在几个月时间发现主题,他们可以看到代码 – EKS 2009-08-26 09:27:22

回答

3

你还没有新的项目,所以你不能在析构函数中删除它。假设你需要“栈”是一个动态调整大小类,那么项目数组必须动态地分配在这种情况下,对项目申报应

Type *items; (as hacker mentions above) 

和构造应该叫“项目=新类型[ARRAY_SIZE]”分配内存,或者至少最初将“项目”指针分配给NULL。

编辑基于评论 -

完成并确保对这个类,你还应该包括一个拷贝构造函数和分配新的内存和副本存储在项目的新对象的值赋值运算符的内存分配责任。这避免了指针被编译器生成的拷贝构造函数或赋值操作符简单地复制,这会导致多个对象指向相同的动态分配的内存。在销毁这些对象中的第一个时,该内存将被删除。其他共享指针的对象进一步使用现在删除的内存将可能导致崩溃。

不是在这里添加代码,而是我参考Martin的回答中的代码this question

+1

你需要完成这篇文章。目前写作并不安全。如果在构造函数中使用new并在析构函数中删除,则还必须定义复制构造函数和赋值运算符,否则obejct的副本将导致双重删除。请参阅:http://stackoverflow.com/questions/255612/c-dynamically-allocating-an-array-of-objects/255744#255744 – 2009-08-26 15:35:00

+0

PS。建议OP使用矢量会更安全。 – 2009-08-26 15:35:50

+0

@Martin York - 在这种情况下,为什么不使用'std :: stack'?这显然是一个学习练习,所以你的第一个评论可能在这里更合适:所有的“非平凡的析构函数”方法必须在这个类中定义或隐藏。 – 2009-08-26 16:09:58

6

您应该只有delete[]你明确分配了new[]。你的items成员不是一个动态分配的数组,因此你不能像释放它一样释放它。

在另一方面,你有

Type items[]; 

,它实际上并不在堆栈的对象实例分配任何空间。

4

我不认为这是内存泄漏!这是因为您删除了非堆对象而发生的崩溃。

+0

准确。 Stack类实例是一个自动变量,然后它的析构函数试图删除[]成员变量数组。 – sharptooth 2009-08-26 09:13:10

1

我看到的第一件事就是你可能意味着Type *items而不是Type items[]

然后您不希望(不能)在动态分配的数据上使用sizeof

2

项目是指向Type的指针。该指针在使用前必须初始化为 (它是wild pointer,因为它是)。那 是你的程序崩溃的原因。它发生在 析构函数中是巧合。它也可以早些发生 。例如。在push()中,内存被项目碰巧指向的 位置覆盖。

您需要为一些元素分配内存。见下文。

您已经拥有 阵列的动态增长逻辑。但是C++中的数组并不知道它的大小为 (它只是某种类型的指针)。您需要保持当前容量/大小的 。因此而不是

sizeof(items) 

定义一个新成员来保存当前容量。

E.g.:

int capacity;

,并在构造函数中:

capacity = 100; 
items = new Type[capacity]; 

最后一行分配数量的元素,并且是主要解决您的问题。

并将其推入():

if (top == capacity/sizeof(Type)) { 

再分配的逻辑将需要改变。例如为:

capacity *= 2; 
items = new Type[capacity]; 

代替
项=新类型[2 *顶部];

我刚刚在这里勾画出了一个解决方案。你可以填写细节。


注意:你只有一个在程序栈的实例;主体()的主体是:

Stack<double> st; 

如果您改为将一个堆栈实例分配给另一个实例,

{ 
    Stack<double> st; 
    Stack<double> st2 = st; //Copy constructor will be invoked for st2. If not defined the compiler generated copy constructor will do a copy of the pointer value. 
    Stack<double> st3; 
    st3 = st; //Assignment operator will be invoked for st3. If not defined the compiler generated assignment operator will do a copy of the pointer value. 
} 

然后堆栈拷贝构造函数(为ST2工作)和 赋值运算符(为ST3工作)必须正确定义 (请参考的成员 项目的复印件)。否则,会在 st,st2和st3超出作用域的范围时,析构函数中的双重或三重删除以及未定义的行为(例如崩溃)。

+0

“但是C++中的数组并不知道它的大小”是正确的,但只对于动态(堆)数组和堆栈数组(大小在编译时固定),你可以计算出数组的大小是多少(sizeof(array)/sizeof(array [0])) 一般来说,不要打扰,应该使用std :: vector(或者boost :: array,如果你需要的话)。 – 2009-08-26 11:00:50

+0

此外,您也没有解释复制构造和赋值操作符的问题。没有这些,你的回答充其量就是危险的。最好的建议是使用std :: vector。 – 2009-08-26 15:38:42

+0

@Martin约克:感谢您指出。我现在在回答中添加了一个部分来解释这些问题。 – 2009-08-28 09:16:51