我花在2小時內找到此內存泄漏:爲什麼C++子類中的字符串會導致內存泄漏?
class Parent{
...
}
class Child:public Parent{
std::string str;
...
}
int main(){
Parent *ptr=new Child();
delete ptr;
}
我由字符串移動到父類固定它。爲什麼會發生這種內存泄漏?兒童的成員是否也不應該被刪除?
我花在2小時內找到此內存泄漏:爲什麼C++子類中的字符串會導致內存泄漏?
class Parent{
...
}
class Child:public Parent{
std::string str;
...
}
int main(){
Parent *ptr=new Child();
delete ptr;
}
我由字符串移動到父類固定它。爲什麼會發生這種內存泄漏?兒童的成員是否也不應該被刪除?
發生這種情況可能是因爲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反映這是未定義的行爲。在我匆忙之中,我沒有提到它,只提到了典型的行爲
實際上,根據C++標準,使用基類指針刪除派生對象是未定義行爲,並且基類的析構函數不是虛擬的。 – PaulMcKenzie
@PaulMcKenzie經常「未定義的行爲」可以是一致的和可預測的,即使你不能依賴它。虛擬析構函數的缺乏很可能導致問題中提到的症狀。 –
@馬克:答案的第二句話表明有定義的行爲(「只有父刪除」),事實上這是不正確的,正如保羅所說。 – MSalters
父母是否有虛擬析構函數?你正在刪除一個Parent *,所以除非它的析構函數是虛擬的,否則它不會調用Child ::〜Child() – Tas
注意,如果'Parent'沒有虛析構函數,'delete'是Undefined Behavior,這比內存泄漏。 – aschepler