AssertValid與Dump的介紹

 CObject::AssertValid 成員函數提供對對象內部狀態的運行時檢查。儘管從 CObject 派生類時不需要重寫 AssertValid,但可以通過重寫使您的類更安全可靠。AssertValid 應在對象的所有成員變量上執行斷言,以驗證它們包含有效值。例如,它應檢查指針成員變量不爲 NULL。

下面的示例顯示如何聲明 AssertValid 函數:
class CPerson : public CObject
{
protected:
   CString m_strName;
   float   m_salary;
public:
#ifdef _DEBUG
   virtual void AssertValid() const;   // Override
#endif
   // ...
};
當重寫 AssertValid 時,在執行您自己的檢查之前請調用 AssertValid 的基類版本。然後使用 ASSERT 宏檢查您的派生類特有的成員,如下所示:

#ifdef _DEBUG
void CPerson::AssertValid() const
{
   // call inherited AssertValid first
   CObject::AssertValid();

   // check CPerson members...
   ASSERT( !m_strName.IsEmpty()); // Must have a name
   ASSERT( m_salary > 0 ); // Must have an income
}
#endif
如果任何成員變量存儲對象,則可以使用 ASSERT_VALID 宏測試它們的內部有效性(如果它們的類重寫了 AssertValid)。

例如,考慮 CMyData 類,該類在其成員變量之一中存儲了一個 CObList。CObList 變量 m_DataList 存儲了一個 CPerson 對象的集合。CMyData 的簡化聲明如下所示:

class CMyData : public CObject
{
   // Constructor and other members ...
   protected:
      CObList* m_pDataList;
   // Other declarations ...
   public:
#ifdef _DEBUG
      virtual void AssertValid( ) const; // Override
#endif
   // Etc. ...
};
CMyData 中重寫的 AssertValid 如下所示:

#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
   // Call inherited AssertValid
   CObject::AssertValid( );
   // Check validity of CMyData members
   ASSERT_VALID( m_pDataList );
   // ...
}
#endif
CMyData 使用 AssertValid 機制測試其數據成員中存儲的對象的有效性。CMyData 中重寫的 AssertValid 爲它自己的 m_pDataList 成員變量調用 ASSERT_VALID 宏。

因爲 CObList 類也重寫 AssertValid,所以有效性測試不在該級別停止。該重寫對列表的內部狀態執行附加有效性測試。因此,對 CMyData 對象的有效性測試將導致對存儲的 CObList 列表對象內部狀態的附加有效性測試。

再多進行一些操作,還可以添加對存儲在列表中的 CPerson 對象的有效性測試。可以從 CObList 派生 CPersonList 類,並重寫 AssertValid。在重寫中可調用 CObject::AssertValid,然後循環訪問列表,在列表中存儲的每個 CPerson 對象上調用 AssertValid。本主題開始所示的 CPerson 類已重寫了 AssertValid。

當爲調試生成時,這是一種功能極強的機制。當接着爲發佈生成時,該機制自動關閉。

AssertValid 的限制
給定類的 AssertValid 函數的用戶應注意該函數的限制。觸發的斷言指示對象一定有誤,並且執行將暫停。但是,缺少斷言只指示未找到任何問題,並不保證對象是好的。

當從 CObject 派生類時,在使用 DumpAllObjectsSince 將對象轉儲到“輸出”窗口時,可以重寫 Dump 成員函數以提供附加信息。

Dump 函數將對象的成員變量的文本化表示形式寫入轉儲上下文 (CDumpContext)。轉儲上下文類似於 I/O 流。可以使用插入運算符 (<<) 向 CDumpContext 發送數據。

重寫 Dump 函數時,應先調用 Dump 的基類版本以轉儲基類對象的內容。然後爲派生類的每個成員變量輸出文本化說明和值。

Dump 函數的聲明如下所示:

class CPerson : public CObject
{
public:
#ifdef _DEBUG
   virtual void Dump( CDumpContext& dc ) const;
#endif

   CString m_firstName;
   CString m_lastName;
   // And so on...
};
由於對象轉儲只在調試程序時有意義,所以 Dump 函數的聲明用 #ifdef _DEBUG / #endif 塊括起來。

在下面的示例中,Dump 函數先爲其基類調用 Dump 函數。然後,它將每個成員變量的簡短說明與該成員的值一起寫入診斷流。

#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
   // Call the base class function first.
   CObject::Dump( dc );

   // Now do the stuff for our specific class.
   dc << "last name: " << m_lastName << "\n"
      << "first name: " << m_firstName << "\n";
}
#endif
必須提供 CDumpContext 參數以指定轉儲輸出的目的地。MFC 的“Debug”版本提供名爲 afxDump 的預定義 CDumpContext 對象,它將輸出發送到調試器。

CPerson* pMyPerson = new CPerson;
// Set some fields of the CPerson object.
//...
// Now dump the contents.
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif
在 MFC 程序中,可以使用 DumpAllObjectsSince 轉儲有關堆中尚未釋放的所有對象的說明。DumpAllObjectsSince 轉儲自上個 CMemoryState::Checkpoint 以來分配的所有對象。如果未發生 Checkpoint 調用,則 DumpAllObjectsSince 將轉儲當前在內存中的所有對象和非對象。

注意   必須先啓用診斷跟蹤,然後才能使用 MFC 對象轉儲。
注意   程序退出時 MFC 將自動轉儲所有泄漏的對象,因此不必創建代碼在該點轉儲對象。
以下代碼通過比較兩個內存狀態來測試內存泄漏,並在檢測到泄漏時轉儲所有對象:

if( diffMemState.Difference( oldMemState, newMemState ) )
{
   TRACE( "Memory leaked!\n" );
   diffMemState.DumpAllObjectsSince();
}
轉儲的內容如下所示:

Dumping objects ->

{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4

Last Name: Smith
First Name: Alan
Phone #: 581-0215

{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
大多數行開始處的大括號中的數字指定對象的分配順序。最近分配的對象具有最高編號,並顯示在轉儲的頂部。

發佈了7 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章