剛開學, 比較忙, 沒有太多時間寫網誌了, 今天分享一個前幾天自己在寫 C++ 的時候遇到的一個關於記憶體方面的坑吧
錯誤的動態記憶體配置在 C++ 程式設計中非常常見, 導致其的主要原因是指標的亂用、隨意回收、隨意配置等. 昨天在寫 C++ 程式碼的時候, 誤將 operator new
與 delete[]
混合使用. 其實在保證絕對安全的情況之下, 這兩者混合使用確是沒有什麼問題的, 但是就怕水平不夠
我們來看一個例子
class Foo {
private:
int *p;
public:
Foo() : p {new int} {}
~Foo() {
delete this->p;
}
};
這種含有指標的類別在 C++ 中特別多, 尤其是我們在寫資料結構的時候, 我們為了保證低層的速度, 我們通常直接使用 C++ 內建的指標進行操作, 而不對指標機型封裝或者使用已經封裝好的智慧指標
我們來看看如果對 Foo 類別混合使用 operator new
和 delete[]
會怎麼樣
class Foo {
private:
int *p;
public:
Foo() : p {new int} {}
~Foo() {
delete this->p;
}
};
int main(int argc, char *argv[]) {
auto arr {reinterpret_cast<Foo *>(::operator new (sizeof(Foo) * 10))};
delete[] arr;
}
下面是記憶體檢測工具 Valgrind 給出的檢測結果

為什麼會出現這樣的情況呢?
因為使用 operator new
配置的記憶體和 malloc
一樣, 都是未初始化的, 而對於未初始化的指標, 其指向的記憶體位址是未知的, 而且肯定也不是 nullptr
, 所以當我們使用 delete[]
時, 就會產生 Invalid Free
這個時候我們需要稍微深究 delete[]
的行為
因為 Invalid Free 錯誤的出現, 從而肯定了 delete[]
訪問了 Foo 類別的解構子
因此, 我們可以從中推斷 :
delete[]
首先按照申請的指標的大小對每一個申請的元素進行解構, 之後一次性釋放記憶體, 這是導致 Invalid Free 的根本原因
因此, 我們應該儘量避免混用 operator new
與 delete
但是有一些例外, 對於具有 trivial destructor 的型別來說, 混用一般來說不出現問題
int main(int argc, char *argv[]) {
auto arr {reinterpret_cast<int *>(::operator new (sizeof(int) * 10))};
delete[] arr;
}
此時, Valgrind 並沒有給出任何錯誤

不過, 這裡只是 Clang 給出的結果, Clang 可能會對此進行特殊的優化, 具體以 C++ 標準為準
自創文章, 原著 : Jonny, 如若需要轉發, 在已經授權的情況下請註明出處 :《new、operator new、delete 與 operator delete》https://jonny.vip/2018/09/16/new%e3%80%81operator-new%e3%80%81delete-%e8%88%87-operator-delete/
Leave a Reply