Overriding the AssertValid Function

The AssertValid member function is provided in CObject to allow run-time checks of an object’s internal state. AssertValid typically performs assertions on all the object’s member variables to see if they contain valid values. For example, AssertValid can check that all pointer member variables are not NULL. If the object is invalid, AssertValid halts the program.

Although you are not required to override AssertValid when you derive your class from CObject, you can make your class safer and more reliable by doing so. The following example shows how to declare the AssertValid function in the class declaration:

class CPerson : public CObject
{
protected:   
    CString m_strName;
    float   m_salary;
public:
#ifdef _DEBUG
    virtual void AssertValid() const;    // Override
#endif
    // ...
};

When you override AssertValid, first call AssertValid for the base class. Then use the ASSERT macro to check the validity of the members unique to your derived class, as shown by the following example:

#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

If any of the member variables of your class store objects, you can use the ASSERT_VALID macro to test their internal validity (if their classes override AssertValid). The following example shows how this is done.

Consider a class CMyData, which stores a CObList in one of its member variables. The CObList variable, m_DataList, stores a collection of CPerson objects. An abbreviated declaration of CMyData looks like this:

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

The AssertValid override in CMyData looks like this:

#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
    // Call inherited AssertValid
    CObject::AssertValid( );
    // Check validity of CMyData members
    ASSERT_VALID( m_pDataList );
    // ...
}
#endif

CMyData uses the AssertValid mechanism to add validity tests for the objects stored in its data member to the validity test of the CMyData object itself. The overriding AssertValid of CMyData invokes the ASSERT_VALID macro for its own m_pDataList member variable.

The chain of validity testing might stop at this level, but in this case class CObList overrides AssertValid too, and the ASSERT_VALID macro causes it to be called. This override performs additional validity testing on the internal state of the list. If an assertion failure occurs, diagnostic messages are printed, and the program halts.

Thus, a validity test on a CMyData object leads to additional validity tests for the internal states of the stored CObList list object. With a little more work, the validity tests could include the CPerson objects stored in the list as well. You could derive a class CPersonList from CObList and override AssertValid. In the override, you would call CObject::AssertValid and then iterate through the list, calling AssertValid on each CPerson object stored in the list. The CPerson class shown at the beginning of this topic already overrides AssertValid.

This is a powerful mechanism when you build for debugging. When you subsequently build for release, the mechanism is turned off automatically.

Users of an AssertValid function of a given class should be aware of the limitations of this function. A triggered assertion indicates that the object is definitely bad and execution will halt. However, a lack of assertion only indicates that no problem was found, but the object isn’t guaranteed to be good.