VC斷言的應用

2021-04-08 19:31:20 字數 3566 閱讀 4128

一、關於斷言

所謂的斷言就是可以肯定為正確的乙個陳述語句。

假設某個函式需要乙個指向文件物件的指標作為引數,但卻錯誤地使用了乙個檢視指標來呼叫該函式。如果函式繼續使用該錯誤位址,輕則程式執行得不到正確的結果,重則破壞檢視資料。由於這種錯誤往往要到使用檢視資料時才會發現,因而要找出導致錯誤的根本原因就要付出相當大的代價了。

只要在函式開始部分加入斷言檢查,檢驗指標是否真正指向乙個文件物件,就可以完全地避免此類問題的產生。

二、assert巨集

assert巨集能夠計算作為引數傳遞的表示式值。如果表示式為真,則執行繼續。否則,程式顯示乙個訊息並中斷。此時可以選擇忽略錯誤、終止程式或進入偵錯程式。下面是如何在函式中應用assert巨集驗證引數的乙個例子:

void foo(char* p,int size)

如果沒有定義_debug預處理符,則該語句不會真正生成**。visual c++會在除錯模式編譯時自動定義_debug,而在發行模式下,該預處理符是不存在的。如果定義了_debug,則上述兩個斷言生成的**類如:

//assert(p != 0);

do while(0);

//assert((size >= 100);

dowhile(0);

do-while結構在乙個單獨的語句塊之內封裝了整個斷言操作。if語句計算斷言表示式,如果值為0則呼叫afxassertfailedline()。afxassertfailedline()顯示訊息框並提供 「abort,retry,or ignore」選擇,如果選擇retry則呼叫afxdebugbreak(),並由此啟用偵錯程式。

和afxassertfailedline()的簡單目的(即顯示對話方塊以供選擇)相比,它的實現顯得非常複雜。該函式的源**在afxasert.cpp內,可以發現它使用了一些特殊的函式以確保訊息框正確顯示。舉個例子,如果斷言失敗是在程式傳送wm_quit訊息之後的某個位置,則afxassertfailedline()為了顯示訊息框必須臨時地從佇列中刪除這個訊息。

傳遞給afxassertfailedline()的引數__file__ 和 __line__分別為代表程式檔名和當前行號的預處理符號。它們由ansi標準定義,由編譯器自動生成具體數值。

三、verify巨集

由於assert僅在程式的除錯版起作用,測試表示式總是被動的。也就是說,它們不能包含賦值、增量、減量等真正改變資料的操作。但有時候我們需要驗證乙個主動表示式,比如賦值語句。這時可以使用verify代替assert。下面是乙個例子:

void foo(char* p,int size)

在除錯模式下assert和verify是相同的。但在發行模式下,verify能夠繼續對表示式求值(但不再進行斷言檢驗),而assert語句在效果上就如同已經刪除了一樣。

儘管在mfc源**中可以找到一些應用verify的例子,但assert用得更為普遍。一些程式設計師總是完全避免使用verify,因為他們已經習慣於使用被動斷言。請記住,如果在assert語句中使用了主動表示式,編譯器不會發出任何警告。在發行模式下編譯時該表示式會被直接刪除,從而導致程式執行的錯誤。由於發行版程式不含除錯資訊,這種型別的錯誤是很難找到原因的。

四、debug_only巨集

可以認為debug_only巨集是assert巨集的一種特殊格式,它用於計算表示式而不執行斷言檢查。這在只為除錯目的而加入某語句時很有用,如:

void foo(char* p,int size,char fill)

在上例中,即使第三個引數非法,除錯過程仍可繼續。事實上,很少有程式設計師喜歡用這個巨集。他們更習慣於使用傳統的方法,即使用_debug預處理符使得除錯**只在除錯時被編譯:

void foo(char* p,int size,char fill)

五、assert_valid巨集

assert在執行簡單驗證時很有用,但對於c++物件,特別是由cobject派生的物件,則有更好的方法來實現類似操作。作為一般規則,我們應在開始使用每乙個物件之前檢查資料訛誤。assert_valid巨集使得對cobject的派生類實現該操作非常簡單,其過程如下所示:

void cmyview::foo(cyourview* pview) // cmyview 和 cyourview 從cobject 繼承

如下所示,這些巨集直接呼叫afxassertvalidobject():

void cmyview::foo(cyourview* pview)

afxassertvalidobject()的定義可以在objcore.cpp檔案找到,它是乙個沒有正式說明文件的mfc函式。afxassertvalidobject()首先檢查指標非空(null)且指向乙個合法的記憶體位址,其大小符合由相關的cruntimeclass物件指定的數值。如果這些測試失敗,則afxassertvalidobject()的行為就像乙個普通斷言錯誤- -即呼叫afxassertfailedline(),有可能還要呼叫afxdebugbreak();否則,afxassertvalidobject()呼叫虛函式assertvalid(),可以覆蓋後者以執行其它的完整性檢查。

如果使用應用嚮導或類嚮導生成基於mfc的類,通常會得到assertvalid()的骨架,最好改寫這些骨架**以增加最基本的完整性檢查。下面是乙個典型的例子,類sample從cobject繼承,假定它含有職員名字及其薪水:

class sample : public cobject

#ifdef _debug

virtual void assertvalid() const;

#endif

#ifdef _debug

void sample::assertvalid() const

#endif

顯然,依賴於派生類的資料成員,這些assertvalid()函式可以更為複雜。由於該函式只在除錯版起作用,因而無需擔心由此產生的時間開銷。然而,在某些熱點如cview::ondraw()函式,mfc會頻繁地驗證其cdocument指標,此時在文件物件內應該避免冗長的測試。當然,如果正確地使用了文件/檢視結構,許多應用資料將會儲存在cdocument的派生類物件中,此時妥協和折衷還是必要的。

六、assert_kindof巨集

許多時候只需要驗證指標確實引用了所希望的物件型別,以確保可以安全地訪問該物件的成員。assert_kindof能夠完成該檢查,而且時間開銷比assert_valid要少。其呼叫形式如下:

void sample::dosomething(cmydocument* pdoc) const

assert_kindof要求所檢查的指標指向從cobject派生的物件,而且該物件實現mfc執行時類資訊宣告。這種宣告通常是通過在類宣告中包含declare_dynamic巨集,在實現檔案包含implement_dynamic巨集來實現的。應用嚮導(或類嚮導)能夠為大多數文件和檢視類自動生成該宣告。

下面是由assert_kindof生成的真正**:

void sample::dosomething(cmydocument* pdoc) const

{//assert_kindof(cmydocument,pdoc) 生成以下**

assert(pdoc->iskindof(runtime_class(cmydocument)));

// 現在可以確認 pdoc 指向 cmydocument 物件

// 其它操作

在VC 中使用斷言

在 vc 中使用斷言 斷言是乙個巨集,格式為 assert 邏輯表示式 booleanexpression 邏輯表示式可以是任意乙個表示式,其值是 或者非 從程式碼的可讀性角度來講,這個表示式應該是不包含與布林型變數的邏輯比較操作的。斷言僅僅在 debug 過程中產生作用。當邏輯表示式出現了 fal...

VC除錯狀態,斷言ASSERT 的使用

assert 是乙個除錯程式時經常使用的巨集,在程式執行時它計算括號內的表示式,如果表示式為false 0 程式將報告錯誤,並終止執行。如果表示式不為0,則繼續執行後面的語句。這個巨集通常原來判斷程式中是否出現了明顯非法的資料,如果出現了終止程式以免導致嚴重後果,同時也便於查詢錯誤。例如,變數n在程...

python斷言的實際應用

python的unittest模組提供了乙個測試框架,只要我們寫乙個繼承unittest.testcase的類,類中用setup做初始化,用teardown做清理。主要用到的函式有 failedinfo表示不成立列印資訊failedinfo,為可選引數 self.fail msg 會無條件的導致測試...