安全程式設計 c 野指標和記憶體洩漏

2021-09-06 07:31:42 字數 2910 閱讀 4249

儘管c++ 野指標和記憶體洩漏一直被詬病,但是在實時性很強的應用場合,c++ 仍然是不二之選。遊戲伺服器開發仍然使用c++ 作為主語言,但是大多結合動態指令碼技術,一方面規避了野指標和記憶體洩露,一方面獲得了開發效率和擴充套件性的紅利。但指令碼技術不是本文的討論重點,事實上關於c++ 與 lua的技術文章我也一直在整理中,將會另文別述。今天主要說說在使用c++過程中,如何避免和解決野指標和記憶體洩漏問題。

野指標的出現會導致程式崩潰,這是每個人都不願意看到的。linux會生成coredump檔案,可用gdb分析。win下可以註冊unexception獲取呼叫堆疊,將錯誤資訊寫到檔案中。先分析一下通常出現野指標的場景:

class monster_t}}

問題就在於,m_attack有值,但是對應的物件已經被銷毀了。這是大部分野指標出現原因。分析類之間關係可知,monster_t 和 player_t是0-1的關係,monster_t引用player_t,但是player_t甚至都不知道有乙個(或n個)monster 引用了自己。所以當player被銷毀時,很難做到把所有引用該player_t的地方全部重置。這種問題其實比較常見,比如player中引用connection,而connection又是被網路層管理生命週期的,也同樣容易產生野指標情況。常見的解決方式是:

class monster_t}}

另外一種與之相似的方式:

class monster_t

else}}

梳理野指標的產生原因後,我們其實需要的是這樣的指標:

一種指標,引用了另乙個物件的位址(不然就不是指標了),當目標物件銷毀時,該指標自然指向null,而不需要目標物件主動通知重置。

幸運的是,這種指標已經有了,就是weak_ptr; 在boost庫中,sharedptr,scopedptr,weakptr統稱為smartptr。可以盡量使用智慧型指標,避免野指標。本人建議盡量使用shared_ptr結合weak_ptr使用。scoped_ptr本人使用的較少,只是在建立執行緒物件的時候使用,正好符合不能複製的語義。使用shared_ptr和weak_ptr的示例**:

class

monster_t

public

:

void

handle_ai()}}

有人問monster_t為什麼不直接使用shared_ptr,如果使用shared_ptr就不符合現實的模型了,monster_t顯然不應該控制player_t的生命週期,如果使用了shared_ptr,那麼可能導致player_t被延遲析構,甚至會導致記憶體暴漲。這也是shared_ptr的使用誤區,所以本人建議盡量shared_ptr和weak_ptr結合用,否則野指標問題解決了,記憶體洩漏問題又來了。

野指標問題可以通過採用良好的程式設計正規化,盡量規避,但總計c++規避記憶體洩漏的方法卻很為難,簡單而言盡量保證物件的分配和釋放(分別)是單個入口的,這樣大部分問題都可以攔截在code review階段。那麼怎麼檢測記憶體洩漏呢?

首先說明本方法區別於valgrind等工具,該工具是除錯期進行的檢測,本文**的是執行期的檢測,確切說是執行期定時輸出所有物件的數量到日誌中。

首先定義分配、釋放物件的介面:

templatet* new_obj()

templatet* new_obj(arg1 arg1)

templatet* new_obj(arg1 arg1, arg2 arg2)

templatet* new_array(int n)

為了節省篇幅,這裡只列舉了三種構造的**,當分配物件時,對應的型別數量增加1,obj_counter 使用原子操作為每一種型別記錄其數量。

class obj_counter_i

virtual ~ obj_counter_i(){}

void inc(int n)

void dec(int n)

long val() const

virtual string get_name()

protected:

volatile long m_ref_count;

};templateclass obj_counter_t: public obj_counter_i

virtual string get_name()

};

相應的當物件被釋放的時候,對應的物件數量減一,示例**如下:

templatevoid del_obj(t* p)

}

這樣就做到了所有的物件的數量都被記錄了,可以定時的將物件數量輸出到檔案:

class obj_counter_summary_t

mapget_all_obj_num()

return ret;

}void dump(const string& path_)

;snprintf(buff, sizeof(buff), "obj,num,%s\n", tmp_buff);

tmp_fstream << buff;

for (; it != ret.end(); ++it)

tmp_fstream.flush();

}protected:

listm_all_counter;

};

檔案內容data:

總結:

全部示例**:

關於 記憶體洩漏,野指標

記憶體洩漏 動態申請的記憶體空間沒有正常釋放,但也不能繼續使用 char a new char a char b new char b a b a 的空間未被釋放,也無法訪問,形成記憶體洩漏 基類的析構函式應設定為virtual,否則基類指標指向子類物件,呼叫基類析構時不會呼叫子類的析構函式,而造成...

記憶體洩漏與野指標

當你要為變數a申請一塊記憶體空間的時候,需要用到malloc函式。如何申請這塊記憶體空間呢?這就變成程式的工作了。程式首先會向系統申請一塊記憶體空間,然後程式會把申請好的這塊記憶體空間的首位址賦給變數a,假設這時候變數a拿到的首位址為0x1234,如果我們現在要對變數a進行迴圈操作,那麼每迴圈一次,...

c 徹底消滅 記憶體洩漏 野指標 下篇

6.後記 前篇最後,我們為消除記憶體洩漏 野指標等問題所做的 嘗試還是存在問題,本篇我們來討論一下進一步的改進。使用者只new開闢,但是忘記使用delete釋放,程式需要定期自動清理記憶體。在之前的記憶體管理中,部分指標需要用delete,部分指標不能用delete,這導致使用者的困惑。後續改進中,...