2012-06-30 32 views
0

我正在寫一個動態數組,它保存着每個索引中值的指針。我正在嘗試編寫takeAt方法,但我不確定它是否會導致內存泄漏。通過引用返回存儲爲指針的值 - 內存泄漏?

指針數組是「清單」 T** list = new T* [SIZE];

段:

template <class T> 
const T& List<T>::takeAt(const int &index) { 
    if (isEmpty()) { 
     return T(); 
    } 
    assert(index >= 0 && index < size); 
    T* ptr = list[index]; 
    int numMove = size - index; 
    memmove(list + index, list + index + 1, numMove * POINTER_SIZE); 
    list[size] = NULL; 
    size--; 
    return *ptr; 
} 

如果這樣做會導致內存泄漏,我怎麼能解決這個問題(不返回指針代替)? 請注意別的地方我做錯了。提前致謝。

+0

什麼是「列表」,它存儲在哪裏/如何? – orlp

回答

2

內存泄漏不在該函數中,而可能是該函數的調用者可能無法刪除返回的對象。但是,仍然存在一些問題,其中最大的問題是功能簽名完全誤導了它的功能。將const引用返回給對象使其看起來好像只是訪問它(ala std::vector::at或類似),而不是獲取所有權。你還會返回一個臨時引用,這會導致後面的麻煩(你的編譯器不會對此發出警告嗎?)。

對於這樣的函數,您應該返回一個指針,這將使得調用者獲取返回對象的所有權更清晰。喜歡的東西:

template <class T> T* List<T>::takeAt(const int index) { 
    if (isEmpty()) { 
     return NULL; 
    } 
    ... 
    return ptr; 
} 

這也可以讓你返回NULL如果對象是不可比返回一個默認構造的對象(因爲調用者可以告訴不是可用的對象之間的差異更明智並且簡單地得到一個處於默認狀態的)。另外,爲什麼可以在空容器上調用這個函數而不是索引大於它的大小呢?這兩起案件看起來與我相當。

小問題:不要用像const這樣的簡單類型來引用const。我不會在這裏詳細討論這些細節,但是更值得一提的是它們更具有地道性。

最後,我會建議考慮這個模型是否真的有意義;你可能會注意到沒有一個標準容器提供這樣的功能。這很容易被濫用,並且看起來像是一個內存泄漏和混亂的肥沃來源。

+0

返回'NULL'指針是C風格的(並且需要C風格的錯誤檢查)。如果存在範圍錯誤,我寧願拋出'std :: out_of_range'(注意索引一個空數組實際上是超範圍錯誤)。 – orlp

+0

公平召集;我傾向於同意,雖然我現在的僱主禁止例外,所以也許我會擺脫這種習慣。無論哪種方式都比返回默認構造的實例更好。 – Peter

1

事實上,你正在返回一個參考不是一個問題。你只需要在你的函數之外釋放內存,以防內存在堆中。

int index = 4; 
T &value = list_var.takeAt(index); 
// ... 
delete &value; 

請注意,上面的代碼預計T沒有析構函數。引用是如何訪問內存的一種方式。它與分配的方式沒有關係。

除此之外,您在第一次調用時將NULL置於數組外部。另外,不需要將NULL放入指針數組中。這不會改變任何事情。

0

這不是直接存在內存泄漏,一旦函數退出但您似乎很有可能成爲一個並且似乎相當危險的數據,您仍然有辦法引用所有數據。

一旦您從該函數返回,您仍然可以在返回的引用上執行&以獲取其地址,以便您可以刪除該項目。但這不是一種常見的做事方式,而是容易出錯。

這一切都取決於您在列表中存儲的內容。如果他們是你新分配的東西,那麼可能是泄漏。如果它們是現有對象的地址,那麼它們可能不需要刪除或可能在別處被刪除,因此不會泄露任何東西。

另外,我不會打電話給我的數組「列表」。這很可能會給C++程序員帶來很多困惑,他們會將其作爲std :: list模板讀取。