常見記憶體洩露及解決方案 選自ood啟示錄

2021-08-21 23:07:28 字數 4069 閱讀 5412

常見記憶體洩露及解決方案-選自ood啟示錄

new/delete, array new/arrray delete匹配

case 1:

在類的建構函式與析構函式中沒有匹配地呼叫 new/delete!

解決方法:檢查建構函式,在出現new的情況下,按相反的順序在析構函式中匹配新增delete!

這裡有兩個意思:

1〉new與delete匹配,array new/array delete匹配;

2〉出現在前面的new要比出現在後面的new後匹配各自的delete;

比如:建構函式:

m_x = new int[10];

...m_y = new cstring;

則析構函式:

delete m_y;

...delete m_x;// 對於基本資料型別,用delete也可以,但為了統一,還// 是用array delete

case 2:

沒有正確地清除巢狀的物件指標

也就是說,某個物件以引用語義(指標)了包含另乙個物件,而不是以值的方式。

解決辦法:

1〉養成好的成對編碼習慣:

在外部函式分配的堆記憶體,不要在呼叫函式裡面釋放,而在外部函式內釋放;

2〉盡量在建構函式裡面分配記憶體,並注意不要犯case 1錯誤;

3〉在基類/繼承類各管各的記憶體;(具體解析見下面的case 8)

for example:

#include

#include

// melon : 甜瓜,西瓜;

class melon

;melon::melon(char * var)

melon::~melon()

void melon::print()

// meal : 進餐;

class meal

;meal::meal(char * var, char * res)

// 方法2:改引用為值包含;

// : m_melon(var)

meal::~meal()

void meal::print()

int main(...)

case 3:在釋放物件陣列時,沒有使用delete ;

1>對於單個物件,單個基本型別(如int,double等)的變數,我們肯定採用delete,不會出錯;

2>對於基本型別陣列,由於不需要大小引數,因而,採用delete或array delete(delete ),均可以,如上例中,我便直接採用了delete m_variety,建議為了統一,採用delete m_variety;

3>對於自定義的物件所組成的物件陣列,則一定要採用array delete,這樣編譯器才會在釋放記憶體前呼叫每個物件的析構函式,並呼叫

free釋放物件陣列空間;

for example:

#include

#include

class point

;point::point(int x, int y, char *col)

:m_x(x), m_y(y)

point::~point()

int main(int argc, char *argv)

case 4:

指向由指向物件的指標構成的陣列不等同於與物件陣列。

也就是說,陣列的基本型別是指向物件的指標,此時,是用delete 還是delete (array delete),並不重要,關鍵是指標並沒有析構函式,必須使用者自己呼叫delete語句.

for example:

// point類和case 3一樣;

int main(int argc, char *argv)

// 下面語句並沒有釋放10個point物件,釋放的只是他們的指標所組成的陣列

// 占用的10*sizeof(point*) 空間,造成了記憶體洩露

// (180 = 10*sizeof(point) + 10* 6; (6= sizeof("green")))

//delete pptrary;

// 正確的方法:

for (i = 0; i < 10; ++i)

deletepptrary; // 或者delete pptrary;

return 0;

}case 5:

缺少拷貝建構函式

這沒什麼好說的,主要是解決編譯器預設新增的拷貝建構函式不足!預設的拷貝建構函式採用位拷貝,

如下**:

point x;

point y(x);

這樣會導致兩個point物件 x,y的 m_color指向同乙個"red"字串;

當某個物件釋放後,另外乙個物件的 m_color變成懸空指標,從而導致程式異常;

解決方法:

編寫自己的拷貝建構函式;

對於point類,編寫如下:

point::point(const point& y)

: m_x(y.m_x), m_y(y.m_y)

case 6:

缺少過載賦值運算子,理由和上面一樣!

需要注意其實現的細節區別:

1> 拷貝建構函式編譯器會自動阻止自己構造自己,比如:

point x(x); // 出錯;

但是,賦值操作不會;

point x = x; // 編譯期不會出錯,但執行期會出錯!

上面的錯誤原因在於,編譯器雖然為x分配了記憶體,但呼叫拷貝建構函式時,m_color還沒初始化;

建議,盡量不要用這種方法初始化,以便將錯誤在編譯期間顯示出來;

2> 賦值運算子必須區別是否自身賦值;

3> 在賦值前必須釋放原有new操作分配的資源(當然,其他檔案等資源也要釋放,這裡只討論記憶體溢位,略過不提!)

最後實現如下:

const point& point::operator =(const point& rhs)

return *this;

}注意,最左邊的const宣告可以不要,要得話是為了阻止如下語句:

(x = y) = z;

但由於基本型別也支援,為了與基本型別一致,可以去掉const約束;

case 7:

關於nonmodifying運算子過載的常見錯誤;

所謂nonmodifying運算子就是不改變運算元的值,並且返回結果型別與運算元一樣;比如數**算符;

而關係運算子則不滿足,因為其結果為bool型;

賦值運算子也不是(=, += ,<<=等等);

主要原因是,大家可能將結果儲存到乙個區域性變數裡面,而返回結果為了效率採用了引用(&);

解決方法:

1> 利用static, 將臨時變數作為類的內部儲存單元;

不足,不適合巢狀使用和多執行緒,比如 w = x+y+z;

for example:

// case 7,解決方法1:static

const point& point::operator +(const point& rhs) const

注意,這裡為了簡單,並沒有考慮型別轉換,實際中二元運算子通常採用友元函式形式實現,具體判斷方法請看effective c++ item 19;

2> 改引用語義為值語義;(最好辦法,但會降低效率)

注意,有人也許會用指標方法,比如如下:

point *temp = new point;

...return (*temp);

這樣會產生乙個無名物件,並且位於堆上,從而造成記憶體洩露;

const point point::operator +(const point& rhs) const

case 8:

沒用將基類的析構函式定義成虛函式;

解決方法:

將基類的析構函式定義為虛函式;

注意:1> 該錯誤具有隱蔽性,當所有派生類均沒有新的new操作時,不會產生記憶體溢位;因而,最好遵循以下原則:

將基類建構函式定義為非虛函式,則該類不允許擴充套件;

2> 如果不是虛函式,則釋放基類指標不會呼叫派生類的析構函式,即使它指向乙個派生類物件;

3> 不管是不是虛函式,釋放派生類指標均會呼叫基類的析構函式,且呼叫順序不變;

4> 如果為虛函式,則釋放基類指標且該指標指向乙個派生類,則會先呼叫派生類的析構函式,再呼叫基內的析構函式!

常見記憶體洩露及解決方案

常見記憶體洩露及解決方案 常見記憶體洩露及解決方案 選自ood啟示錄 new delete,array new arrray delete匹配 case 1 在類的建構函式與析構函式中沒有匹配地呼叫 new delete!解決方法 檢查建構函式,在出現new的情況下,按相反的順序在析構函式中匹配新增...

C 常見記憶體錯誤及解決方案

c 中記憶體錯誤通常屬於執行時錯誤,只有在程式執行時才能發現,編譯器無法自動檢測到記憶體錯誤。多數情況下是程式邏輯或者引數存在某些錯誤。下面總結一下c 常見的記憶體錯誤 1.記憶體洩露 記憶體洩露是指應用程式未釋放動態申請的且不再使用的記憶體,原因可能是程式設計師疏忽或者錯誤造成程式異常。在c c ...

C 常見記憶體錯誤及解決方案

c 常見記憶體錯誤及解決方案 c 中記憶體錯誤通常屬於執行時錯誤,只有在程式執行時才能發現,編譯器無法自動檢測到記憶體錯誤。多數情況下是程式邏輯或者引數存在某些錯誤。下面總結一下c 常見的記憶體錯誤 1.記憶體洩露 記憶體洩露是指應用程式未釋放動態申請的且不再使用的記憶體,原因可能是程式設計師疏忽或...