Delen via


C/C++ Asserties

Een bewering geeft een voorwaarde aan die u verwacht waar te zijn op een bepaald moment in uw programma. Als deze voorwaarde niet waar is, mislukt de assertie, wordt de uitvoering van uw programma onderbroken en wordt het dialoogvenster Assertion Failed weergegeven.

Visual Studio ondersteunt C++-assertie-instructies die zijn gebaseerd op de volgende constructies:

  • MFC-asserties voor MFC-programma's.

  • ATLASSERT voor programma's die GEBRUIKMAKEN van ATL.

  • CRT-asserties voor programma's die gebruikmaken van de C-runtime-bibliotheek.

  • De ANSI-assertfunctie voor andere C/C++-programma's.

    U kunt asserties gebruiken om logische fouten te ondervangen, de resultaten van een bewerking te controleren en foutvoorwaarden te testen die moeten zijn verwerkt.

In dit onderwerp

Hoe asserties werken

Asserties in builds voor foutopsporing en release

Bijwerkingen van het gebruik van asserties

CRT-asserties

MFC-asserties

Hoe asserties werken

Wanneer het foutopsporingsprogramma stopt vanwege een MFC- of C-runtimebibliotheekassertie, en als de bron beschikbaar is, navigeert het foutopsporingsprogramma naar het punt in het bronbestand waar de assertie is opgetreden. Het bevestigingsbericht wordt weergegeven in zowel het uitvoervenster als het dialoogvenster Assertion Failed . U kunt het bevestigingsbericht uit het uitvoervenster naar een tekstvenster kopiëren als u het bericht wilt opslaan voor toekomstig gebruik. Het venster Uitvoer kan ook andere foutberichten bevatten. Bekijk deze berichten zorgvuldig, omdat ze aanwijzingen geven voor de oorzaak van de fout in de assertie.

Gebruik asserties om fouten tijdens de ontwikkeling te detecteren. Gebruik in de regel één verklaring voor elke aanname. Als u bijvoorbeeld ervan uitgaat dat een argument niet NULL is, gebruikt u een verklaring om die aanname te testen.

In dit onderwerp

Asserties in builds voor foutopsporing en release

Assertie-instructies worden alleen gecompileerd als _DEBUG is gedefinieerd. Anders behandelt de compiler asserties als nul-opdrachten. Daarom leggen assertieverklaringen geen overhead- of prestatiekosten in uw definitieve release-programma op en kunt u het gebruik van #ifdef instructies vermijden.

Bijwerkingen van het gebruik van asserties

Wanneer u asserties aan uw code toevoegt, moet u ervoor zorgen dat de asserties geen bijwerkingen hebben. Denk bijvoorbeeld aan de volgende bewering die de nM waarde wijzigt:

ASSERT(nM++ > 0); // Don't do this!

Omdat de ASSERT expressie niet wordt geëvalueerd in de releaseversie van uw programma, nM hebben verschillende waarden in de versies Foutopsporing en Release. Als u dit probleem in MFC wilt voorkomen, kunt u de macro VERIFY gebruiken in plaats van ASSERT. VERIFY evalueert de expressie in alle versies, maar controleert het resultaat niet in de releaseversie.

Wees vooral voorzichtig met het gebruik van functieaanroepen in assertie-instructies, omdat het evalueren van een functie onverwachte bijwerkingen kan hebben.

ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe

VERIFY roept myFnctn aan in zowel de debug- als de releaseversie, dus het is acceptabel om te gebruiken. Door het gebruik van VERIFY wordt echter een onnodige functieaanroep als overhead in de Releaseversie opgelegd.

In dit onderwerp

CRT-asserties

Het CRTDBG.H headerbestand definieert de _ASSERT en _ASSERTE macro's voor assertiecontrole.

Macroniveau Resultaat
_ASSERT Als de opgegeven uitdrukking ONWAAR is, worden de bestandsnaam en het regelnummer van de _ASSERT weergegeven.
_ASSERTE Hetzelfde als _ASSERT, plus een tekenreeksweergave van de expressie die geassert is.

_ASSERTE is krachtiger omdat het de geclaimde uitdrukking rapporteert die ONWAAR bleek te zijn. Dit kan voldoende zijn om het probleem te identificeren zonder naar de broncode te verwijzen. De Debug-versie van uw toepassing bevat echter voor elke geverifieerde expressie een tekenreeksconstante met behulp van _ASSERTE. Als u veel _ASSERTE macro's gebruikt, nemen deze tekenreeksexpressies een aanzienlijke hoeveelheid geheugen in beslag. Als dat een probleem blijkt te zijn, kunt u _ASSERT gebruiken om geheugen te besparen.

Wanneer _DEBUG is gedefinieerd, wordt de _ASSERTE macro als volgt gedefinieerd:

#define _ASSERTE(expr) \
    do { \
        if (!(expr) && (1 == _CrtDbgReport( \
            _CRT_ASSERT, __FILE__, __LINE__, #expr))) \
            _CrtDbgBreak(); \
    } while (0)

Als de assertie-expressie ONWAAR evalueert, wordt _CrtDbgReport aangeroepen om de assertiefout te melden (standaard met behulp van een berichtdialoogvenster). Als u Opnieuw proberen kiest in het berichtdialoogvenster, retourneert _CrtDbgReport 1 en roept _CrtDbgBreak het foutopsporingsprogramma aan DebugBreak.

Als u alle asserties tijdelijk wilt uitschakelen, gebruikt u _CtrSetReportMode.

Controleren op Heap-corruptie

In het volgende voorbeeld wordt _CrtCheckMemory gebruikt om te controleren op beschadiging van de heap:

_ASSERTE(_CrtCheckMemory());

Controle van geldigheid van pointers

In het volgende voorbeeld wordt _CrtIsValidPointer gebruikt om te controleren of een bepaald geheugenbereik geldig is voor lezen of schrijven.

_ASSERTE(_CrtIsValidPointer( address, size, TRUE );

In het volgende voorbeeld wordt _CrtIsValidHeapPointer gebruikt om te controleren of een aanwijzer verwijst naar het geheugen in de lokale heap (de heap die is gemaakt en beheerd door dit exemplaar van de C-runtimebibliotheek: een DLL kan een eigen exemplaar van de bibliotheek hebben, en daarom een eigen heap, buiten de heap van de toepassing). Deze bewering onderschept niet alleen null- of out-of-bounds-adressen, maar ook aanwijzingen naar statische variabelen, stackvariabelen en ander niet-lokaal geheugen.

_ASSERTE(_CrtIsValidHeapPointer( myData );

Een geheugenblok controleren

In het volgende voorbeeld wordt _CrtIsMemoryBlock gebruikt om te controleren of een geheugenblok zich in de lokale heap bevindt en een geldig bloktype heeft.

_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));

In dit onderwerp

MFC-asserties

MFC definieert de ASSERT-macro voor assertiecontrole. Het definieert ook de MFC ASSERT_VALID en CObject::AssertValid methoden voor het controleren van de interne status van een CObject-afgeleide object.

Als het argument van de MFC-macro ASSERT resulteert in nul of onwaar, stopt de macro de uitvoering van het programma en waarschuwt de gebruiker. Anders wordt de uitvoering voortgezet.

Wanneer een assertie mislukt, wordt in een berichtdialoogvenster de naam van het bronbestand en het regelnummer van de assertie weergegeven. Als u Opnieuw kiest in het dialoogvenster, zorgt een aanroep van AfxDebugBreak ervoor dat de uitvoering wordt onderbroken naar de debugger. Op dat moment kunt u de aanroepstack onderzoeken en andere foutopsporingsfaciliteiten gebruiken om te bepalen waarom de assertie is mislukt. Als u Just-In-Time-foutopsporing hebt ingeschakeld en het foutopsporingsprogramma nog niet is uitgevoerd, kan het dialoogvenster het foutopsporingsprogramma starten.

In het volgende voorbeeld ziet u hoe ASSERT u de retourwaarde van een functie controleert:

int x = SomeFunc(y);
ASSERT(x >= 0);   //  Assertion fails if x is negative

U kunt ASSERT gebruiken met de functie IsKindOf om typecontrole van functieargumenten op te geven:

ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );

De ASSERT macro produceert geen code in de releaseversie. Als u de expressie in de releaseversie wilt evalueren, gebruikt u de macro VERIFY in plaats van ASSERT.

MFC-ASSERT_VALID en CObject::AssertValid

De methode CObject::AssertValid biedt runtimecontroles van de interne status van een object. Hoewel u niet hoeft te overschrijven AssertValid wanneer u uw klas afleidt van CObject, kunt u uw klas betrouwbaarder maken door dit te doen. AssertValid moet asserties uitvoeren op alle lidvariabelen van het object om te controleren of ze geldige waarden bevatten. Er moet bijvoorbeeld worden gecontroleerd of aanwijzerlidvariabelen niet NULL zijn.

In het volgende voorbeeld ziet u hoe u een AssertValid functie declareert:

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

Wanneer u AssertValid overschrijft, roept u de versie van de basisklasse van AssertValid aan voordat u uw eigen controles uitvoert. Gebruik vervolgens de macro ASSERT om de leden te controleren die uniek zijn voor uw afgeleide klasse, zoals hier wordt weergegeven:

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

    // Check CPerson members...
    // Must have a name.
    ASSERT( !m_strName.IsEmpty());
    // Must have an income.
    ASSERT( m_salary > 0 );
}
#endif

Als een van uw lidvariabelen objecten opslaat, kunt u de ASSERT_VALID macro gebruiken om hun interne geldigheid te testen, mits hun klassen AssertValid overschrijven.

Denk bijvoorbeeld aan een klasse CMyData, waarin een CObList wordt opgeslagen in een van de lidvariabelen. De CObList variabele, m_DataListslaat een verzameling CPerson objecten op. Een verkorte verklaring van CMyData ziet er als volgt uit:

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

De AssertValid overschrijving ziet er in CMyData als volgt uit:

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

CMyData gebruikt het AssertValid mechanisme om de geldigheid te testen van de objecten die zijn opgeslagen in het gegevenslid. Het overschrijven AssertValid van CMyData roept de ASSERT_VALID macro aan voor zijn eigen lidvariabele m_pDataList.

Geldigheidstests stoppen niet op dit niveau omdat de klasse CObList ook overschrijft AssertValid. Met deze overschrijving wordt een aanvullende geldigheidstest uitgevoerd op de interne toestand van de lijst. Een geldigheidstest op een CMyData object leidt dus tot aanvullende geldigheidstests voor de interne statussen van het opgeslagen CObList lijstobject.

Met wat meer werk kunt u ook geldigheidstests toevoegen voor de CPerson objecten die zijn opgeslagen in de lijst. U kunt een klas CPersonList afleiden van CObList en overschrijven AssertValid. In de method override roept u CObject::AssertValid aan en dan door de lijst itereren, en AssertValid aanroept voor elk CPerson object dat in de lijst is opgeslagen. De CPerson klasse die aan het begin van dit onderwerp wordt weergegeven, overschrijft AssertValidal.

Dit is een krachtig mechanisme wanneer u bouwt voor foutopsporing. Wanneer u vervolgens bouwt voor release, wordt het mechanisme automatisch uitgeschakeld.

Beperkingen van AssertValid

Een geactiveerde assertie geeft aan dat het object zeker slecht is en dat de uitvoering wordt gestopt. Een gebrek aan assertie geeft echter alleen aan dat er geen probleem is gevonden, maar het object is niet gegarandeerd goed.

In dit onderwerp

Asserties gebruiken

Logische fouten detecteren

U kunt een verklaring instellen op een voorwaarde die waar moet zijn volgens de logica van uw programma. De assertie heeft geen effect tenzij er een logische fout optreedt.

Stel dat u gasmoleculen in een container simuleert en dat de variabele numMols het totale aantal moleculen vertegenwoordigt. Dit getal mag niet kleiner zijn dan nul, dus u kunt een MFC-assertie-instructie als volgt opnemen:

ASSERT(numMols >= 0);

U kunt ook een CRT-assertie als volgt opnemen:

_ASSERT(numMols >= 0);

Deze instructies doen niets als uw programma correct werkt. Als een logische fout numMols kleiner is dan nul, stopt de assertie echter de uitvoering van uw programma en wordt het dialoogvenster Assertion Failed weergegeven.

In dit onderwerp

Resultaten controleren

Asserties zijn waardevol voor testbewerkingen waarvan de resultaten niet duidelijk zijn bij een snelle visuele inspectie.

Denk bijvoorbeeld aan de volgende code, waarmee de variabele iMols wordt bijgewerkt op basis van de inhoud van de gekoppelde lijst waarnaar wordt verwezen:mols

/* This code assumes that type has overloaded the != operator
 with const char *
It also assumes that H2O is somewhere in that linked list.
Otherwise we'll get an access violation... */
while (mols->type != "H2O")
{
    iMols += mols->num;
    mols = mols->next;
}
ASSERT(iMols<=numMols); // MFC version
_ASSERT(iMols<=numMols); // CRT version

Het aantal moleculen dat wordt geteld, iMols moet altijd kleiner dan of gelijk zijn aan het totale aantal moleculen, numMols. Visuele inspectie van de lus toont niet aan dat dit per se het geval zal zijn, dus er wordt na de lus een assertiestatement gebruikt om op die voorwaarde te testen.

In dit onderwerp

Niet-verwerkte fouten vinden

U kunt asserties gebruiken om te testen op foutvoorwaarden op een punt in uw code waar eventuele fouten moeten zijn verwerkt. In het volgende voorbeeld retourneert een grafische routine een foutcode of nul voor succes.

myErr = myGraphRoutine(a, b);

/* Code to handle errors and
   reset myErr if successful */

ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version

Als de code voor foutafhandeling correct werkt, moet de fout worden verwerkt en myErr opnieuw worden ingesteld op nul voordat de assertie wordt bereikt. Als myErr een andere waarde heeft, mislukt de bewering, stopt het programma en verschijnt het dialoogvenster Assertion Failed.

Echter, assertie-instructies zijn geen vervanging voor foutafhandelingscode. In het volgende voorbeeld ziet u een assertie-instructie die kan leiden tot problemen in de definitieve releasecode:

myErr = myGraphRoutine(a, b);

/* No Code to handle errors */

ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!

Deze code is afhankelijk van het assertiestatement om de foutvoorwaarde af te handelen. Als gevolg hiervan wordt elke foutcode die wordt geretourneerd door myGraphRoutine , niet verwerkt in de definitieve releasecode.

In dit onderwerp