2016-03-07 75 views
1

幾天前,我看到一個關於C++中內存泄漏的採訪問題。 代碼就像(如果我沒記錯的話):如何避免設計實現級別的內存泄漏

#include <iostream> 
using namespace std; 

class super { 
    int value; 
    int arr[1000]; 
public: 
    super() :value(0) {} 
    super(int value) :value(value) {} 
    virtual int getValue() const{ 
     return this->value; 
    } 
}; 

class sub : public super { 
    int val; 
    super sup; 
    vector<int> v1; 
public: 
    sub() :val(0), sup(0) {} 
    sub(int value) :val(value), sup(value), v1(10,0) {} 
    int getValue() const{ 
     return this->val; 
    } 
}; 

int main() { 
    sub* pt1 = new(sub); 
    super* pt2 = pt1; 

    pt1 = new(sub); 

    delete pt2;  //memory leak ?? 

    //more code here... 
    delete pt1; 
    return 0; 
} 

的問題是如何避免這種類型的內存泄漏在實現的設計水平。我想這個問題不僅僅是回答「不要使用像這樣的指針」。

它是否與將析構函數實現爲虛擬或使用動態轉換相關?我們如何實現析構函數,以便delete pt2不會造成任何內存泄漏?任何人都可以進一步分析此示

在此先感謝。

+3

我不明白這是如何泄漏內存。您正在通過指向其基類的指針刪除第一個「sub」對象,這應該沒問題,因爲'sub'不包含任何資源。如果是這樣,你必須在'super'中聲明一個虛擬析構函數。 –

+0

我猜虛擬析構函數在這裏是缺少的請參閱http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors – kwarnke

+0

@RolandW是的我不記得這個例子的代碼。隨意編輯問題中的代碼,以便子類可以保存資源。 – BugShotGG

回答

2

首先,delete pt2;不是專門的內存泄漏。我最初說這是未定義的行爲,因此標準允許任何事情,包括內存泄漏。但仔細查看這些類,實際上對於int陣列的第一版代碼,sub是可以破壞的細節,因此,看起來很奇怪,這段代碼是正確的。然後,您更改了代碼以使sub不再受到輕微的破壞(由於數據成員爲vector),因此現在它是未定義的行爲。

誰設置的問題可能一直在尋找一個明確的答案,如果是這樣,我不知道那是什麼,但在不止一種方法這個錯誤可能是「設計走」的人:

  1. 使虛擬super的析構函數,以便通過父指針刪除sub作品。一個常見的經驗法則是說具有虛擬函數的類應該總是具有虛擬析構函數,因爲這樣的類被設計爲通過指向基類的指針/引用來使用。
  2. 在用戶代碼中,使用RAII技術來確保正確銷燬。在這種情況下,智能指針。 shared_ptr可讓您編寫shared_ptr<super> pt2(new sub());即使使用非虛擬析構函數,該對象也會被正確刪除,儘管有些人認爲該功能模糊不清。
  3. 這很難稱之爲「實現設計」決定,因爲「不會造成嚴重的編碼錯誤」不是設計規則,而是語言規則。但是調用代碼可以「設計」爲不通過指向其基類的指針來刪除對象,除非它是有效的(對於這個類它不是)。類可以通過一致的文檔來幫助解決這個問題。
  4. 在類別supersub中,使用vector<int>而不是普通的int陣列。這使得對象本身更小,將大部分數據存儲在外部,因此對於像這個例子那樣的用途,用戶並不覺得他們必須動態分配對象,而是可以將它們放在堆棧上(不是1000個內部必然是不能進入堆棧,只是它的大小可能會讓用戶感到緊張)。因此,如果您對super做出了與sub相同的更改,那麼調用代碼可以通過使其成爲設計原則(儘可能使用自動變量而不是動態分配)來降低此類錯誤的風險。
+0

我想我搞砸了這個問題。我看到的問題肯定是內存泄漏問題,但在這裏我是通過內存編寫代碼的,所以它可能有點不對。 – BugShotGG

0

這是不是經典的內存泄漏。經典的內存泄漏會有內存分配和不釋放。你的新/刪除對實際上是匹配的。

這是未定義的行爲。你會看到一個相當長的解釋here爲什麼有一個public但不是virtual析構函數會產生問題。

隨着他們特定的編譯器,這威力泄漏內存,因爲編譯器做了最好的它可以表現在不確定的行爲面前理智。另一個編譯器或其他版本的編譯器可能會炸燬或創建粉紅色的獨角獸。這不是一個內存泄漏的好例子,因爲內存泄漏是編譯器在惡劣情況下可以產生的最好的。

0

如果您要求在設計級別解決方案的討論可以得到非常廣泛的。
我相信你並沒有真正討論如何實現析構函數。

如何避免「內存泄漏」的設計水平的討論已經由專家在C++領域所做的:StroustrupSutter

我建議查看他們的演示視頻並閱讀他們的文章。

1

因爲super沒有虛析構函數(virtual ~super() = default;)它泄漏內存。

現在當你調用刪除指向一個subsuper*sub的析構函數不叫,泄露其資源。

總是聲明基類析構函數是虛擬的,如果從它派生的任何類有任何資源可以釋放。