2009-01-24 59 views
6

我有一個C++內存管理疑問,這是(顯然)與引用和指針相關。假設我有一類Class與方法my_method這會導致C++中的內存泄漏嗎?

OtherClass& Class::my_method(...) { 
    OtherClass* other_object = new OtherClass(...); 
    return *other_object; 
} 

同時在附近的一段代碼:

{ 
    Class m(...); 
    OtherClass n; 
    n = m.my_method(...); 
} 

所以,我知道,有關於指針一般規則(〜「什麼新 - ed,必須是delete -d「)以避免內存泄漏。但基本上我正在引用我的堆分配對象,所以當n超出範圍時,不應該調用OtherClass的析構函數,從而釋放先前由other_object指向的內存? 所以最後真正的問題是:這會導致內存泄漏嗎?

謝謝。

回答

6

這很明顯,你想返回一個新的對象給調用者,你不需要保留任何引用。爲此,最簡單的方法是按值返回對象。

OtherClass Class::my_method(...) { 
    return OtherClass(...); 
} 

然後在調用代碼中,你可以像這樣構造新的對象。

{ 
    Class m(...); 
    OtherClass n(m.mymethod(...)); 
} 

這樣可以避免返回引用臨時對象或要求客戶端刪除返回的指針的任何顧慮。請注意,這確實需要您的對象是可複製的,但這是一個合法且通常實現的優化,可以在按值返回時避免該副本。

如果您需要共享所有權或對象的生命週期超出調用函數的範圍,那麼您只需要考慮共享指針或類似指針。在後一種情況下,您可以將此決策留給客戶,並仍然按照價值回報。

E.g.

{ 
    Class m(...); 

    // Trust me I know what I'm doing, I'll delete this object later... 
    OtherClass* n = new OtherClass(m.mymethod(...)); 
} 
5

在C++中調用析構函數並釋放內存是兩件截然不同的事情。

delete確實都調用析構函數並釋放內存。 delete[]調用析構函數爲分配的元素數量,然後釋放內存。

當OtherClass超出範圍時,將調用析構函數,但內存不會被釋放。作爲一個建議,當你覺得你已經徹底理解了C++中的指針時,可以看看智能指針,例如,增強智能指針以減輕C++中的內存管理生命。 (例如,請參見article以獲得介紹)

+0

感謝您指點。 – tunnuz 2009-01-24 11:45:39

12

是的,這會導致內存泄漏。

你要做的是,在return語句中,取消引用你創建的新對象。編譯器將調用賦值運算符作爲返回的一部分,並將新對象的CONTENTS複製到調用方法中分配給它的對象。

新對象將留在堆上,其指針從堆棧中清除,從而造成內存泄漏。

爲什麼不返回一個指針並以這種方式管理它?

+0

「編譯器將調用賦值運算符作爲返回的一部分」是不正確的。代碼正在返回一個引用。沒有需要(或完成)的副本來創建參考。 – Suma 2009-01-24 18:04:37

+0

我的不好;返回將返回對新對象的引用(也可能是一個指針),方法調用中的=會調用賦值運算符。結果仍然是相同的:) – 2009-01-25 07:04:05

2

你有2個OtherClass對象:

一個是N,這是當超出範圍在棧上創建,刪除成功。

另一個是你在堆上創建的my_method。這個永遠不會被刪除,並且會導致內存泄漏。

2

通常,當本地對象超出範圍時,它的內存是已釋放只是因爲它被分配到堆棧並自動清除堆棧。由於你的對象被分配在堆上,所以它不可能被自動釋放。解除它的唯一方法是明確地調用delete。

我想你也許可以做到這一點:

聲明另一個類DummyClass,其中包含一個公共成員是一個指向OtherClass對象。在DummyClass的構造函數中,清除指向NULL的指針。在您的函數中,聲明DummyClass類型的對象及其成員指針,以創建另一個類型爲OtherClass的對象。然後在DummyClass的析構函數中,檢查指針是否爲NULL,如果不是,則刪除它。通過這種方式,當DummyClass對象超出範圍時,將自動清理對象。

2

如果可能,您可以考慮使用std :: auto_ptr或boost/c0x shared_ptr來簡化內存管理。

2

如果你堅持堆棧分配,不使用new運營商my_method()並傳遞一個參考,而不是n,即:

void Class::my_method(OtherClass& other_object, ...) { 
    other_object.init(...); 
} 

然後調用my_method()這樣的:

{ 
    Class m(...); 
    OtherClass n; 
    m.my_method(n, ...); 
} 

對於這種模式的工作,Class必須實施某種init()方法od,它允許在不調用構造函數的情況下正確初始化對象。