2009-08-26 156 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