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
大多數行開始處的大括號中的數字指定對象的分配順序。最近分配的對象具有最高編號,並顯示在轉儲的頂部。