VC调试方法
?????????VC所依賴的開發(fā)平臺Microsoft?Developer?Studio本身提供的調(diào)試功能并不弱,每當(dāng)我們創(chuàng)建一個(gè)新的VC工程項(xiàng)目時(shí),默認(rèn)狀態(tài)就是Debug(調(diào)試)版本,在"組建"(Build)菜單下的Configurations中可以看到除了調(diào)試版本還可以設(shè)置成發(fā)行(Release)版本。調(diào)試版本由于包含了大量信息,所以它生成的可執(zhí)行程序容量會(huì)遠(yuǎn)遠(yuǎn)大于發(fā)行版。
?????????具體地,調(diào)試版本主要增加了兩個(gè)內(nèi)容:其一,會(huì)執(zhí)行編譯命令_D_DEBUG,使頭文件的調(diào)試語句ifdef及其代碼附加到程序中;其二,在可執(zhí)行文件中加入的調(diào)試信息使開發(fā)人員能夠觀察變量,進(jìn)行單步執(zhí)行等。在VC"組建"(Build)菜單下的"開始調(diào)試"中有4條專用的調(diào)試命令:Go,Step?into,Run?to?Cursor,Attach?to?process…。在運(yùn)行程序源代碼時(shí)用Go命令(而不是Execute)才能處于調(diào)試狀態(tài),?Go命令會(huì)使程序運(yùn)行變得緩慢下來,但可以更好地控制運(yùn)行程序,我們可以在任何時(shí)刻中斷程序、單步執(zhí)行、查看變量、檢查調(diào)用棧。
????????有必要詳細(xì)介紹一下VC的調(diào)試功能:首先,再次強(qiáng)調(diào)要用Go命令運(yùn)行一個(gè)將要調(diào)試的程序;如果要中止調(diào)試狀態(tài)下的運(yùn)行程序可以點(diǎn)擊Stop?Debugging命令,還可以通過Break選項(xiàng)以可恢復(fù)方式中斷調(diào)試程序的運(yùn)行流程(用Restart選項(xiàng)可以重新開始運(yùn)行程序);Step?Into選項(xiàng)表示每次只執(zhí)行一行語句(單步執(zhí)行),但如果當(dāng)前代碼是調(diào)用一個(gè)函數(shù),那么Step?Into表示進(jìn)入該函數(shù),全部函數(shù)語句執(zhí)行完后返回,而Step?Over則是跳出這個(gè)函數(shù);Step?To?Cursor選項(xiàng)表示程序?qū)?zhí)行到光標(biāo)所在的可執(zhí)行語句行上;在調(diào)試多線程程序時(shí),可以在線程函數(shù)或主應(yīng)用程序線程中設(shè)置斷點(diǎn),還可以用Break選項(xiàng)結(jié)束線程后用Threads選項(xiàng)查看運(yùn)行線程列表,也可以選擇懸掛和恢復(fù)每個(gè)線程;在設(shè)置斷點(diǎn)后,在VC?"查看"菜單的"調(diào)試窗口"中可以查看變量、內(nèi)存、調(diào)用堆棧、寄存器以及反匯編語句。在程序中設(shè)置斷點(diǎn)的方法是,點(diǎn)擊要設(shè)置的代碼行并點(diǎn)擊設(shè)置代碼的工具欄按鈕,會(huì)出現(xiàn)在代碼行最左邊的一個(gè)小黑點(diǎn)即是斷點(diǎn)標(biāo)志,這時(shí)再選Go程序會(huì)在執(zhí)行到端點(diǎn)處停下來,如果要繼續(xù)執(zhí)行可以再選Go。
??????????通過選擇VC"工具"菜單下的"源瀏覽器"可以生成一個(gè).BSC文件,使用瀏覽器可以從中發(fā)現(xiàn)多種信息:程序中任何一個(gè)變量、函數(shù)、類或宏在何處定義及引用;可以列出所有聲明的函數(shù)類、變量、宏;可以發(fā)現(xiàn)調(diào)用一個(gè)指定函數(shù)的所有函數(shù);可以找到一個(gè)指定類的派生來源或者它派生出哪些類。
在使用微軟程序開發(fā)庫MSDN時(shí),我們會(huì)發(fā)現(xiàn)其中的VC示例經(jīng)常采用看似多余的ASSERT語句,其作用就是使程序具有"維護(hù)"性。對于Debug版本的VC程序,在遇到布爾值為FALSE的ASSERT語句處停止,并顯示Assertion?Failed對話;如果設(shè)置為發(fā)布版,所有ASSERT語句都會(huì)被預(yù)處理程序刪除。一個(gè)地道的VC編程員,應(yīng)該有意在自己的代碼中通過"維護(hù)"特征去檢測任何設(shè)定,諸如輸入?yún)?shù)、循環(huán)范圍和變量值的設(shè)定。
在安裝好VC系統(tǒng)之后,在VC之外的程序組中有一個(gè)程序Tracer是一個(gè)跟蹤工具,在激活它后使用Go運(yùn)行VC代碼,在輸出窗口就能夠看到程序運(yùn)行過程中的內(nèi)部過程,包括DLL調(diào)用等,你如果看不到任何輸出,可以轉(zhuǎn)到菜單"查看"(View)點(diǎn)擊"輸出"(Output)。
????????????其實(shí),MFC自身就提供有錯(cuò)誤查找和TRACE語句,而TRACE語句的語法與printf非常類似,所以我們可以在程序中直接加入這條跟蹤命令,如下所示:
//?Example?for?TRACE
int?i?=?1;
char?sz[]?=?"one";
TRACE(?"Integer?=?%d,?String?=?%s\n",?i,?sz?);
//?Output:?'Integer?=?1,?String?=?one'
在Developer?Studio中還提供了一個(gè)ERRLOOK工具,程序員只要輸入錯(cuò)誤號就能得到系統(tǒng)出錯(cuò)信息或模塊錯(cuò)誤內(nèi)容.
MFC從Cobject派生的每個(gè)類都包含一個(gè)Dump函數(shù),該函數(shù)可把當(dāng)前狀態(tài)轉(zhuǎn)儲(Dumping)到輸出窗口,這在某些調(diào)試過程中會(huì)有用,以下代碼是Dump函數(shù)的用法:
//?Example?for?CObject::Dump
void?CAge::Dump(?CDumpContext?&dc?)?const
{
?CObject::Dump(?dc?);
?dc?<<?"Age?=?"?<<?m_years;
}
???????????在MFC中還有一個(gè)非常有用的類是CMemoryState,我們可以在程序的任何部分使用這個(gè)類檢測內(nèi)存沖突,并得到內(nèi)存沖突的確切位置。CMemoryState類有3個(gè)成員函數(shù):CheckPoint可將堆的當(dāng)前狀態(tài)存入類的實(shí)體;Difference可以比較兩個(gè)實(shí)體包含的堆之間的差異;DumpStatistics用于標(biāo)準(zhǔn)化轉(zhuǎn)儲所有被CheckPoint捕獲后分配到堆的對象,如CheckPoint未被調(diào)用實(shí)體未被初始化時(shí),該函數(shù)將轉(zhuǎn)儲當(dāng)前堆的所有內(nèi)容。以下代碼表示了CMemoryState類的使用方法:
//?Example?for?CMemoryState::CMemoryState,
//?Includes?all?CMemoryState?functions
CMemoryState?msOld,?msNew,?msDif;
msOld.Checkpoint();
CAge*?page1?=?new?CAge(?21?);
CAge*?page2?=?new?CAge(?22?);
msOld.DumpAllObjectsSince();
msNew.Checkpoint();
msDif.Difference(?msOld,?msNew?);
msDif.DumpStatistics();
代碼運(yùn)行的結(jié)果為
Dumping?objects?->
{2}?a?CObject?at?$190A
{1}?a?CObject?at?$18EA
Object?dump?complete.
0?bytes?in?0?Free?Blocks
8?bytes?in?2?Object?Blocks
0?bytes?in?0?Non-Object?Blocks
Largest?number?used:?8?bytes
Total?allocations:?8?bytes?
???????????在MFC類和VC中本身就有"異常情況"這個(gè)概念,并在此基礎(chǔ)上形成它們處理系統(tǒng)錯(cuò)誤和意外的主要機(jī)制。比如當(dāng)系統(tǒng)內(nèi)存分配殆盡時(shí),你的運(yùn)行程序就會(huì)收到內(nèi)存異常的消息。這樣就給了程序員消除異常的機(jī)會(huì)。
MFC中的異常情況主要有:CArchiveException表示檔案文件載入或保存時(shí)出錯(cuò),CDBException屬于數(shù)據(jù)庫錯(cuò)誤,CFileException為文件錯(cuò)誤,CMemoryException為調(diào)用new時(shí)發(fā)生分配錯(cuò)誤,CNotSupportedException表示指定操作不被支持,COleException表示在調(diào)用OLE操作時(shí)出錯(cuò),COleDispatchException表示在OLE自動(dòng)操作時(shí)出錯(cuò),CResourceException表示資源找不到或無法創(chuàng)建,CUserException用于通知用戶錯(cuò)誤。
???????????MFC還包含一系列以Afx-為詞頭的調(diào)試函數(shù):AfxAbort可以在發(fā)生致命錯(cuò)誤時(shí)異常終止程序,AfxCheckMemory可以檢查堆和剩余緩沖池的受損部分;AfxDoForAllClasses重聲明所有CObject的派生類;AfxDoForAllObject重聲明堆上所有CObject派生的對象;AfxEnableMemoryTracking啟用或禁止內(nèi)存追蹤;AfxIsMemoryBlock用于確認(rèn)指針?biāo)竷?nèi)存有效;AfxIsValidAddress用于確認(rèn)地址是駐留在程序的內(nèi)存區(qū)域內(nèi);AfxIsValidString用于確認(rèn)地址所指字符串有效;AfxSetAllocHook用于內(nèi)存分配前進(jìn)行檢測;AfxTraceEnabled啟動(dòng)或禁止輸出跟蹤,AfxTraceFlags則進(jìn)一步定制跟蹤特征。
??????????在我們隨手編制的VC程序中,普遍存在著會(huì)發(fā)生內(nèi)存泄漏的隱患,有些問題程序的痼疾癥狀是在處理數(shù)據(jù)量激增時(shí)陷入癱瘓,更糟的要發(fā)現(xiàn)內(nèi)存泄漏并不容易。首先,我們要明確VC中內(nèi)存泄漏的含義:簡單說就是一個(gè)程序申請得到了一段內(nèi)存卻沒有及時(shí)釋放。比如用new在堆中分配了一個(gè)對象或?qū)ο蠼M卻并沒有調(diào)用delete操作。靈活的指針技術(shù)使內(nèi)存泄漏的原因變得復(fù)雜化,比如改變了保存在一變量中的指針的值后未能刪除指針?biāo)赶虻膬?nèi)存區(qū);當(dāng)內(nèi)存泄漏是來自一個(gè)帶有指針類型成員變量的類時(shí)會(huì)更加困難,因?yàn)楫?dāng)調(diào)用分配指針時(shí)并沒有復(fù)制構(gòu)造函數(shù)/析構(gòu)函數(shù)或運(yùn)算符
?????????為了防止發(fā)生內(nèi)存泄漏這樣棘手的故障,在VC編程時(shí)應(yīng)當(dāng)注意遵循幾個(gè)規(guī)范:其一,如果一個(gè)類包含有指針并且分配了指針值,那么就需要構(gòu)造相應(yīng)的析構(gòu)函數(shù)以刪除該指針;其二,如果一個(gè)函數(shù)分配了一塊內(nèi)存并把該內(nèi)存塊返回給調(diào)用它的函數(shù)使用,那么它返回的必須是一個(gè)指針而非一個(gè)引用,因?yàn)橐貌荒鼙怀绦騽h除;其三,即使一個(gè)函數(shù)分配了一段內(nèi)存并在同一函數(shù)的稍后部分刪除了該內(nèi)存段,也要盡可能將內(nèi)存塊分配到堆棧中;最后,就是決不要試圖改變一個(gè)指針值,除非已經(jīng)刪除指針?biāo)傅膶ο蠡蛲ㄟ^數(shù)組指向了該指針?biāo)赶虻膬?nèi)存,而且也不要對new返回的指針進(jìn)行加1運(yùn)算
???????????每當(dāng)編寫VC程序時(shí),我們都會(huì)處于一個(gè)琳瑯滿目的集成開發(fā)環(huán)境(IDE)中,現(xiàn)實(shí)的真相是我們很多人在這里編程多年,對開發(fā)環(huán)境了解并不全面和細(xì)微。記得王朔的小說中有句話說"穿了多年的外套在不穿時(shí)才發(fā)現(xiàn)它原來還有一個(gè)兜!"。我們在安裝VC時(shí),得到的IDE即Developer?Studio,?VC其實(shí)是Developer?Studio下激活的一個(gè)組件而已,比如微軟的VJ++也是基于Developer?Studio。很少有技術(shù)書籍會(huì)一一介紹Developer?Studio界面元素,也許聰明的程序員輕易就能識別其含義,全部猜對界面圖符的含義并非易事。可是它們對我們了解開發(fā)信息很重要,也與調(diào)試程序有關(guān)聯(lián)。
??????????在Developer?Studio下會(huì)生成多個(gè)文件去保存項(xiàng)目的所有信息:一個(gè)是以.DSW為擴(kuò)展名的項(xiàng)目工作區(qū)文件,它包含項(xiàng)目中所有文件的名稱、文件所在目錄、編譯器和連接器的選項(xiàng)以及項(xiàng)目工作的其它信息;以.DSP為擴(kuò)展名的也是項(xiàng)目記錄文件,.OPT是工作區(qū)選項(xiàng)文件,它包含Developer?Studio的所有個(gè)人設(shè)置?-?包括顏色、字體、工具欄、哪個(gè)文件被打開以及MDI窗口如何被定位和最新調(diào)試中的斷點(diǎn)等。在打開項(xiàng)目工作區(qū)文件時(shí)其它文件隨即會(huì)自動(dòng)打開。在Developer?Studio下可以按類查看代碼,其中的ClassView顯示了應(yīng)用程序中所有的類,每個(gè)類下顯示了成員函數(shù)和數(shù)據(jù)成員,在成員函數(shù)旁有粉紅圖標(biāo),數(shù)據(jù)成員旁是藍(lán)綠色圖標(biāo),保護(hù)類成員的圖標(biāo)旁有一枚鑰匙,私有類成員則有一個(gè)掛鎖圖標(biāo)。
?????????當(dāng)然,在開發(fā)環(huán)境下最主要的工作是輸入編輯程序源代碼,源代碼會(huì)顯示"語法著色"。在缺省情況下,代碼為黑色,夾以綠色的注釋和藍(lán)色的關(guān)鍵字(指VC所保留的public、private、new和int等等)。--這些地球人都知道,但是為了調(diào)試需要,我們還可以指定顏色去顯示字符串、數(shù)字和運(yùn)算符。定義方法是通過Tools菜單下的Options對話框中的Format選項(xiàng)卡設(shè)置。
????????????在Developer?Studio提供的諸多菜單項(xiàng)中,我們往往對少數(shù)菜單避而不用,因?yàn)椴涣私馑鼈兊淖饔梦趾闷嫘臅?huì)造成亂子;還有多個(gè)菜單項(xiàng)都可以達(dá)到目的,至于它們之間的微小差別則不甚了了。比如在編譯和調(diào)試時(shí)常用到Build菜單組,它具有和應(yīng)用程序編譯、運(yùn)行調(diào)試相關(guān)的多項(xiàng)操作:其中的Compile菜單會(huì)編譯當(dāng)前的聚焦文件;Build菜單會(huì)編譯和鏈接所有在項(xiàng)目中修改的文件;Build?All會(huì)編譯鏈接項(xiàng)目中所有文件,包括最近編譯后沒有修改過的文件;Batch?Build用于包含有Debug和Release配置的項(xiàng)目;Clean會(huì)刪除所有的中間和輸出文件,因而項(xiàng)目目錄下僅包含源文件;Debugger?Remote?Connection用于遠(yuǎn)程調(diào)試,即在一臺機(jī)器運(yùn)行程序而在另一臺調(diào)試;Set?Active?Configuration可設(shè)置某個(gè)配置為激活狀態(tài)(Debug或Release);Profiler能夠識別應(yīng)用程序的瓶頸,即找到降低程序執(zhí)行速度的代碼和有關(guān)模塊,為此需要在"工程/設(shè)置/Link"下勾選Profiling。
??????????VC中的警告信息是有級別的。在"工程/設(shè)置"下的"C/C++"選項(xiàng)卡中的警告級別Warning?Level缺省值是3,如果改為更為嚴(yán)格的4級,往往會(huì)產(chǎn)生更多的警告類錯(cuò)誤。在C/C++選項(xiàng)卡中還提供了代碼優(yōu)化欄Optimizations,在你完成漫漫調(diào)試之旅準(zhǔn)備正式發(fā)布前夕,你應(yīng)當(dāng)改動(dòng)此項(xiàng),它提供了一些適合建立發(fā)行版應(yīng)用程序的優(yōu)化設(shè)置。
其實(shí),在多年以前,在軟件業(yè)中針對開發(fā)具有一定規(guī)模軟件項(xiàng)目的情況就出現(xiàn)了軟件工程理論,以此來指導(dǎo)軟件人員樹立團(tuán)隊(duì)協(xié)作意識,進(jìn)而保證軟件項(xiàng)目的協(xié)調(diào)性及進(jìn)度質(zhì)量等。筆者在從事VC開發(fā)中對此很有感觸,而且覺得對于每個(gè)開發(fā)人員自己也應(yīng)當(dāng)具備一定的工程素養(yǎng)。比如,我們在開發(fā)初期時(shí),應(yīng)當(dāng)對所編寫的有價(jià)值的源代碼及時(shí)備份,即使有些代碼在后續(xù)階段似乎沒有用處了也不妨“敝帚自珍”。在VC編程中我經(jīng)常遇到的問題是,一個(gè)不算小的編寫了很久的程序,在稍微擴(kuò)展一點(diǎn)功能時(shí)出現(xiàn)了故障,而最撓頭的時(shí)花了很多時(shí)間也無法排故,所幸的是對原來的程序我還有備份,可以重新再來--功能還要加,只是還一種編程思路去實(shí)現(xiàn)相同的功能。說實(shí)在的,本人在VC開發(fā)中,如果要總結(jié)“解決了多少問題”,不如說很多時(shí)候是采用"游擊戰(zhàn)"巧妙地"繞過"了一些棘手的問題。--尤其在軟件交工處于倒計(jì)時(shí)的開發(fā)后期出現(xiàn)的故障,往往你已經(jīng)沒有時(shí)間去找到故障,而是用前一個(gè)完好的VC?Project去嘗試另一條捷徑
?????????具體地,調(diào)試版本主要增加了兩個(gè)內(nèi)容:其一,會(huì)執(zhí)行編譯命令_D_DEBUG,使頭文件的調(diào)試語句ifdef及其代碼附加到程序中;其二,在可執(zhí)行文件中加入的調(diào)試信息使開發(fā)人員能夠觀察變量,進(jìn)行單步執(zhí)行等。在VC"組建"(Build)菜單下的"開始調(diào)試"中有4條專用的調(diào)試命令:Go,Step?into,Run?to?Cursor,Attach?to?process…。在運(yùn)行程序源代碼時(shí)用Go命令(而不是Execute)才能處于調(diào)試狀態(tài),?Go命令會(huì)使程序運(yùn)行變得緩慢下來,但可以更好地控制運(yùn)行程序,我們可以在任何時(shí)刻中斷程序、單步執(zhí)行、查看變量、檢查調(diào)用棧。
????????有必要詳細(xì)介紹一下VC的調(diào)試功能:首先,再次強(qiáng)調(diào)要用Go命令運(yùn)行一個(gè)將要調(diào)試的程序;如果要中止調(diào)試狀態(tài)下的運(yùn)行程序可以點(diǎn)擊Stop?Debugging命令,還可以通過Break選項(xiàng)以可恢復(fù)方式中斷調(diào)試程序的運(yùn)行流程(用Restart選項(xiàng)可以重新開始運(yùn)行程序);Step?Into選項(xiàng)表示每次只執(zhí)行一行語句(單步執(zhí)行),但如果當(dāng)前代碼是調(diào)用一個(gè)函數(shù),那么Step?Into表示進(jìn)入該函數(shù),全部函數(shù)語句執(zhí)行完后返回,而Step?Over則是跳出這個(gè)函數(shù);Step?To?Cursor選項(xiàng)表示程序?qū)?zhí)行到光標(biāo)所在的可執(zhí)行語句行上;在調(diào)試多線程程序時(shí),可以在線程函數(shù)或主應(yīng)用程序線程中設(shè)置斷點(diǎn),還可以用Break選項(xiàng)結(jié)束線程后用Threads選項(xiàng)查看運(yùn)行線程列表,也可以選擇懸掛和恢復(fù)每個(gè)線程;在設(shè)置斷點(diǎn)后,在VC?"查看"菜單的"調(diào)試窗口"中可以查看變量、內(nèi)存、調(diào)用堆棧、寄存器以及反匯編語句。在程序中設(shè)置斷點(diǎn)的方法是,點(diǎn)擊要設(shè)置的代碼行并點(diǎn)擊設(shè)置代碼的工具欄按鈕,會(huì)出現(xiàn)在代碼行最左邊的一個(gè)小黑點(diǎn)即是斷點(diǎn)標(biāo)志,這時(shí)再選Go程序會(huì)在執(zhí)行到端點(diǎn)處停下來,如果要繼續(xù)執(zhí)行可以再選Go。
??????????通過選擇VC"工具"菜單下的"源瀏覽器"可以生成一個(gè).BSC文件,使用瀏覽器可以從中發(fā)現(xiàn)多種信息:程序中任何一個(gè)變量、函數(shù)、類或宏在何處定義及引用;可以列出所有聲明的函數(shù)類、變量、宏;可以發(fā)現(xiàn)調(diào)用一個(gè)指定函數(shù)的所有函數(shù);可以找到一個(gè)指定類的派生來源或者它派生出哪些類。
在使用微軟程序開發(fā)庫MSDN時(shí),我們會(huì)發(fā)現(xiàn)其中的VC示例經(jīng)常采用看似多余的ASSERT語句,其作用就是使程序具有"維護(hù)"性。對于Debug版本的VC程序,在遇到布爾值為FALSE的ASSERT語句處停止,并顯示Assertion?Failed對話;如果設(shè)置為發(fā)布版,所有ASSERT語句都會(huì)被預(yù)處理程序刪除。一個(gè)地道的VC編程員,應(yīng)該有意在自己的代碼中通過"維護(hù)"特征去檢測任何設(shè)定,諸如輸入?yún)?shù)、循環(huán)范圍和變量值的設(shè)定。
在安裝好VC系統(tǒng)之后,在VC之外的程序組中有一個(gè)程序Tracer是一個(gè)跟蹤工具,在激活它后使用Go運(yùn)行VC代碼,在輸出窗口就能夠看到程序運(yùn)行過程中的內(nèi)部過程,包括DLL調(diào)用等,你如果看不到任何輸出,可以轉(zhuǎn)到菜單"查看"(View)點(diǎn)擊"輸出"(Output)。
????????????其實(shí),MFC自身就提供有錯(cuò)誤查找和TRACE語句,而TRACE語句的語法與printf非常類似,所以我們可以在程序中直接加入這條跟蹤命令,如下所示:
//?Example?for?TRACE
int?i?=?1;
char?sz[]?=?"one";
TRACE(?"Integer?=?%d,?String?=?%s\n",?i,?sz?);
//?Output:?'Integer?=?1,?String?=?one'
在Developer?Studio中還提供了一個(gè)ERRLOOK工具,程序員只要輸入錯(cuò)誤號就能得到系統(tǒng)出錯(cuò)信息或模塊錯(cuò)誤內(nèi)容.
MFC從Cobject派生的每個(gè)類都包含一個(gè)Dump函數(shù),該函數(shù)可把當(dāng)前狀態(tài)轉(zhuǎn)儲(Dumping)到輸出窗口,這在某些調(diào)試過程中會(huì)有用,以下代碼是Dump函數(shù)的用法:
//?Example?for?CObject::Dump
void?CAge::Dump(?CDumpContext?&dc?)?const
{
?CObject::Dump(?dc?);
?dc?<<?"Age?=?"?<<?m_years;
}
???????????在MFC中還有一個(gè)非常有用的類是CMemoryState,我們可以在程序的任何部分使用這個(gè)類檢測內(nèi)存沖突,并得到內(nèi)存沖突的確切位置。CMemoryState類有3個(gè)成員函數(shù):CheckPoint可將堆的當(dāng)前狀態(tài)存入類的實(shí)體;Difference可以比較兩個(gè)實(shí)體包含的堆之間的差異;DumpStatistics用于標(biāo)準(zhǔn)化轉(zhuǎn)儲所有被CheckPoint捕獲后分配到堆的對象,如CheckPoint未被調(diào)用實(shí)體未被初始化時(shí),該函數(shù)將轉(zhuǎn)儲當(dāng)前堆的所有內(nèi)容。以下代碼表示了CMemoryState類的使用方法:
//?Example?for?CMemoryState::CMemoryState,
//?Includes?all?CMemoryState?functions
CMemoryState?msOld,?msNew,?msDif;
msOld.Checkpoint();
CAge*?page1?=?new?CAge(?21?);
CAge*?page2?=?new?CAge(?22?);
msOld.DumpAllObjectsSince();
msNew.Checkpoint();
msDif.Difference(?msOld,?msNew?);
msDif.DumpStatistics();
代碼運(yùn)行的結(jié)果為
Dumping?objects?->
{2}?a?CObject?at?$190A
{1}?a?CObject?at?$18EA
Object?dump?complete.
0?bytes?in?0?Free?Blocks
8?bytes?in?2?Object?Blocks
0?bytes?in?0?Non-Object?Blocks
Largest?number?used:?8?bytes
Total?allocations:?8?bytes?
???????????在MFC類和VC中本身就有"異常情況"這個(gè)概念,并在此基礎(chǔ)上形成它們處理系統(tǒng)錯(cuò)誤和意外的主要機(jī)制。比如當(dāng)系統(tǒng)內(nèi)存分配殆盡時(shí),你的運(yùn)行程序就會(huì)收到內(nèi)存異常的消息。這樣就給了程序員消除異常的機(jī)會(huì)。
MFC中的異常情況主要有:CArchiveException表示檔案文件載入或保存時(shí)出錯(cuò),CDBException屬于數(shù)據(jù)庫錯(cuò)誤,CFileException為文件錯(cuò)誤,CMemoryException為調(diào)用new時(shí)發(fā)生分配錯(cuò)誤,CNotSupportedException表示指定操作不被支持,COleException表示在調(diào)用OLE操作時(shí)出錯(cuò),COleDispatchException表示在OLE自動(dòng)操作時(shí)出錯(cuò),CResourceException表示資源找不到或無法創(chuàng)建,CUserException用于通知用戶錯(cuò)誤。
???????????MFC還包含一系列以Afx-為詞頭的調(diào)試函數(shù):AfxAbort可以在發(fā)生致命錯(cuò)誤時(shí)異常終止程序,AfxCheckMemory可以檢查堆和剩余緩沖池的受損部分;AfxDoForAllClasses重聲明所有CObject的派生類;AfxDoForAllObject重聲明堆上所有CObject派生的對象;AfxEnableMemoryTracking啟用或禁止內(nèi)存追蹤;AfxIsMemoryBlock用于確認(rèn)指針?biāo)竷?nèi)存有效;AfxIsValidAddress用于確認(rèn)地址是駐留在程序的內(nèi)存區(qū)域內(nèi);AfxIsValidString用于確認(rèn)地址所指字符串有效;AfxSetAllocHook用于內(nèi)存分配前進(jìn)行檢測;AfxTraceEnabled啟動(dòng)或禁止輸出跟蹤,AfxTraceFlags則進(jìn)一步定制跟蹤特征。
??????????在我們隨手編制的VC程序中,普遍存在著會(huì)發(fā)生內(nèi)存泄漏的隱患,有些問題程序的痼疾癥狀是在處理數(shù)據(jù)量激增時(shí)陷入癱瘓,更糟的要發(fā)現(xiàn)內(nèi)存泄漏并不容易。首先,我們要明確VC中內(nèi)存泄漏的含義:簡單說就是一個(gè)程序申請得到了一段內(nèi)存卻沒有及時(shí)釋放。比如用new在堆中分配了一個(gè)對象或?qū)ο蠼M卻并沒有調(diào)用delete操作。靈活的指針技術(shù)使內(nèi)存泄漏的原因變得復(fù)雜化,比如改變了保存在一變量中的指針的值后未能刪除指針?biāo)赶虻膬?nèi)存區(qū);當(dāng)內(nèi)存泄漏是來自一個(gè)帶有指針類型成員變量的類時(shí)會(huì)更加困難,因?yàn)楫?dāng)調(diào)用分配指針時(shí)并沒有復(fù)制構(gòu)造函數(shù)/析構(gòu)函數(shù)或運(yùn)算符
?????????為了防止發(fā)生內(nèi)存泄漏這樣棘手的故障,在VC編程時(shí)應(yīng)當(dāng)注意遵循幾個(gè)規(guī)范:其一,如果一個(gè)類包含有指針并且分配了指針值,那么就需要構(gòu)造相應(yīng)的析構(gòu)函數(shù)以刪除該指針;其二,如果一個(gè)函數(shù)分配了一塊內(nèi)存并把該內(nèi)存塊返回給調(diào)用它的函數(shù)使用,那么它返回的必須是一個(gè)指針而非一個(gè)引用,因?yàn)橐貌荒鼙怀绦騽h除;其三,即使一個(gè)函數(shù)分配了一段內(nèi)存并在同一函數(shù)的稍后部分刪除了該內(nèi)存段,也要盡可能將內(nèi)存塊分配到堆棧中;最后,就是決不要試圖改變一個(gè)指針值,除非已經(jīng)刪除指針?biāo)傅膶ο蠡蛲ㄟ^數(shù)組指向了該指針?biāo)赶虻膬?nèi)存,而且也不要對new返回的指針進(jìn)行加1運(yùn)算
???????????每當(dāng)編寫VC程序時(shí),我們都會(huì)處于一個(gè)琳瑯滿目的集成開發(fā)環(huán)境(IDE)中,現(xiàn)實(shí)的真相是我們很多人在這里編程多年,對開發(fā)環(huán)境了解并不全面和細(xì)微。記得王朔的小說中有句話說"穿了多年的外套在不穿時(shí)才發(fā)現(xiàn)它原來還有一個(gè)兜!"。我們在安裝VC時(shí),得到的IDE即Developer?Studio,?VC其實(shí)是Developer?Studio下激活的一個(gè)組件而已,比如微軟的VJ++也是基于Developer?Studio。很少有技術(shù)書籍會(huì)一一介紹Developer?Studio界面元素,也許聰明的程序員輕易就能識別其含義,全部猜對界面圖符的含義并非易事。可是它們對我們了解開發(fā)信息很重要,也與調(diào)試程序有關(guān)聯(lián)。
??????????在Developer?Studio下會(huì)生成多個(gè)文件去保存項(xiàng)目的所有信息:一個(gè)是以.DSW為擴(kuò)展名的項(xiàng)目工作區(qū)文件,它包含項(xiàng)目中所有文件的名稱、文件所在目錄、編譯器和連接器的選項(xiàng)以及項(xiàng)目工作的其它信息;以.DSP為擴(kuò)展名的也是項(xiàng)目記錄文件,.OPT是工作區(qū)選項(xiàng)文件,它包含Developer?Studio的所有個(gè)人設(shè)置?-?包括顏色、字體、工具欄、哪個(gè)文件被打開以及MDI窗口如何被定位和最新調(diào)試中的斷點(diǎn)等。在打開項(xiàng)目工作區(qū)文件時(shí)其它文件隨即會(huì)自動(dòng)打開。在Developer?Studio下可以按類查看代碼,其中的ClassView顯示了應(yīng)用程序中所有的類,每個(gè)類下顯示了成員函數(shù)和數(shù)據(jù)成員,在成員函數(shù)旁有粉紅圖標(biāo),數(shù)據(jù)成員旁是藍(lán)綠色圖標(biāo),保護(hù)類成員的圖標(biāo)旁有一枚鑰匙,私有類成員則有一個(gè)掛鎖圖標(biāo)。
?????????當(dāng)然,在開發(fā)環(huán)境下最主要的工作是輸入編輯程序源代碼,源代碼會(huì)顯示"語法著色"。在缺省情況下,代碼為黑色,夾以綠色的注釋和藍(lán)色的關(guān)鍵字(指VC所保留的public、private、new和int等等)。--這些地球人都知道,但是為了調(diào)試需要,我們還可以指定顏色去顯示字符串、數(shù)字和運(yùn)算符。定義方法是通過Tools菜單下的Options對話框中的Format選項(xiàng)卡設(shè)置。
????????????在Developer?Studio提供的諸多菜單項(xiàng)中,我們往往對少數(shù)菜單避而不用,因?yàn)椴涣私馑鼈兊淖饔梦趾闷嫘臅?huì)造成亂子;還有多個(gè)菜單項(xiàng)都可以達(dá)到目的,至于它們之間的微小差別則不甚了了。比如在編譯和調(diào)試時(shí)常用到Build菜單組,它具有和應(yīng)用程序編譯、運(yùn)行調(diào)試相關(guān)的多項(xiàng)操作:其中的Compile菜單會(huì)編譯當(dāng)前的聚焦文件;Build菜單會(huì)編譯和鏈接所有在項(xiàng)目中修改的文件;Build?All會(huì)編譯鏈接項(xiàng)目中所有文件,包括最近編譯后沒有修改過的文件;Batch?Build用于包含有Debug和Release配置的項(xiàng)目;Clean會(huì)刪除所有的中間和輸出文件,因而項(xiàng)目目錄下僅包含源文件;Debugger?Remote?Connection用于遠(yuǎn)程調(diào)試,即在一臺機(jī)器運(yùn)行程序而在另一臺調(diào)試;Set?Active?Configuration可設(shè)置某個(gè)配置為激活狀態(tài)(Debug或Release);Profiler能夠識別應(yīng)用程序的瓶頸,即找到降低程序執(zhí)行速度的代碼和有關(guān)模塊,為此需要在"工程/設(shè)置/Link"下勾選Profiling。
??????????VC中的警告信息是有級別的。在"工程/設(shè)置"下的"C/C++"選項(xiàng)卡中的警告級別Warning?Level缺省值是3,如果改為更為嚴(yán)格的4級,往往會(huì)產(chǎn)生更多的警告類錯(cuò)誤。在C/C++選項(xiàng)卡中還提供了代碼優(yōu)化欄Optimizations,在你完成漫漫調(diào)試之旅準(zhǔn)備正式發(fā)布前夕,你應(yīng)當(dāng)改動(dòng)此項(xiàng),它提供了一些適合建立發(fā)行版應(yīng)用程序的優(yōu)化設(shè)置。
其實(shí),在多年以前,在軟件業(yè)中針對開發(fā)具有一定規(guī)模軟件項(xiàng)目的情況就出現(xiàn)了軟件工程理論,以此來指導(dǎo)軟件人員樹立團(tuán)隊(duì)協(xié)作意識,進(jìn)而保證軟件項(xiàng)目的協(xié)調(diào)性及進(jìn)度質(zhì)量等。筆者在從事VC開發(fā)中對此很有感觸,而且覺得對于每個(gè)開發(fā)人員自己也應(yīng)當(dāng)具備一定的工程素養(yǎng)。比如,我們在開發(fā)初期時(shí),應(yīng)當(dāng)對所編寫的有價(jià)值的源代碼及時(shí)備份,即使有些代碼在后續(xù)階段似乎沒有用處了也不妨“敝帚自珍”。在VC編程中我經(jīng)常遇到的問題是,一個(gè)不算小的編寫了很久的程序,在稍微擴(kuò)展一點(diǎn)功能時(shí)出現(xiàn)了故障,而最撓頭的時(shí)花了很多時(shí)間也無法排故,所幸的是對原來的程序我還有備份,可以重新再來--功能還要加,只是還一種編程思路去實(shí)現(xiàn)相同的功能。說實(shí)在的,本人在VC開發(fā)中,如果要總結(jié)“解決了多少問題”,不如說很多時(shí)候是采用"游擊戰(zhàn)"巧妙地"繞過"了一些棘手的問題。--尤其在軟件交工處于倒計(jì)時(shí)的開發(fā)后期出現(xiàn)的故障,往往你已經(jīng)沒有時(shí)間去找到故障,而是用前一個(gè)完好的VC?Project去嘗試另一條捷徑
轉(zhuǎn)載于:https://www.cnblogs.com/zzili/archive/2012/12/06/6663383.html
總結(jié)
- 上一篇: 一个应用程序多线程误用的分析
- 下一篇: Windows 搭建 C/C++ 开发环