2015-07-13 71 views
5

我花在2小時內找到此內存泄漏:爲什麼C++子類中的字符串會導致內存泄漏?

class Parent{ 
    ... 
} 

class Child:public Parent{ 
    std::string str; 
    ... 
} 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; 
} 

我由字符串移動到父類固定它。爲什麼會發生這種內存泄漏?兒童的成員是否也不應該被刪除?

+6

父母是否有虛擬析構函數?你正在刪除一個Parent *,所以除非它的析構函數是虛擬的,否則它不會調用Child ::〜Child() – Tas

+4

注意,如果'Parent'沒有虛析構函數,'delete'是Undefined Behavior,這比內存泄漏。 – aschepler

回答

11

發生這種情況可能是因爲Parent可能沒有虛擬析構函數。由於您正在創建Parent*(基類)動態分配派生類Child,刪除沒有虛擬析構函數的Parent*將導致未定義的行爲,但通常會導致派生類不被銷燬。

Scott Myers - Effective C++ Third Edition

......如果我們有一個非虛析構函數刪除基類指針,結果是不確定的。通常在運行時發生的是對象的派生部分永遠不會被銷燬。這是泄漏資源,破壞數據結構並花費大量時間與調試器的絕佳方式。所以任何具有虛函數的類幾乎都必須具有虛析構函數。

class Parent{ 
}; 

class Child:public Parent{ 
public: 
    Child() : str("Child") {} 
    ~Child() { std::cout << str << std::endl;}; 
    std::string str; 
}; 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; // undefined behaviour: deleting a Base* to a Derived object where the Base has no virtual destructor 
} 

您可以通過Parent的析構函數virtual解決這個問題:

class Parent{ 
public: 
    virtual ~Parent() {} // Will call derived classes destructors now as well 
}; 

class Child:public Parent{ 
public: 
    Child() : str("Child") {} 
    ~Child() { std::cout << str << std::endl;}; 
    std::string str; 
}; 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; 
    // Child::~Child() has now been called. 
} 

When to use virtual destructors?這或許可以解釋比我做的更好

編輯:謝謝你的@aschepler(在問題的評論中),下面的評論者以及相關問題的答案,我已更新了答案ter反映這是未定義的行爲。在我匆忙之中,我沒有提到它,只提到了典型的行爲

+3

實際上,根據C++標準,使用基類指針刪除派生對象是未定義行爲,並且基類的析構函數不是虛擬的。 – PaulMcKenzie

+0

@PaulMcKenzie經常「未定義的行爲」可以是一致的和可預測的,即使你不能依賴它。虛擬析構函數的缺乏很可能導致問題中提到的症狀。 –

+0

@馬克:答案的第二句話表明有定義的行爲(「只有父刪除」),事實上這是不正確的,正如保羅所說。 – MSalters