Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
En försäkran anger ett villkor som du förväntar dig ska vara sant vid en tidpunkt i programmet. Om villkoret inte är sant misslyckas försäkran, körningen av programmet avbryts och dialogrutan Försäkran misslyckades visas.
Visual Studio stöder C++-försäkran som baseras på följande konstruktioner:
MFC-påståenden för MFC-program.
ATLASSERT för program som använder ATL.
CRT-satser för program som använder C-körbiblioteket.
Den ANSI assert-funktionen för andra C/C++-program.
Du kan använda påståenden för att fånga logikfel, kontrollera resultatet av en åtgärd och testa felvillkor som borde ha hanterats.
I det här avsnittet
Påståenden i felsöknings- och utgåvor för lansering
Biverkningar av att använda försäkran
Fånga logikfel
Så här fungerar försäkran
När felsökningsprogrammet stoppas på grund av en MFC- eller C-körbiblioteksassertion, navigerar felsökningsprogrammet till den punkt i källfilen där påståendet inträffade om källan är tillgänglig. Uttrycksmeddelandet visas i både utdatafönstret och dialogrutan fel vid uttryckskontroll. Du kan kopiera assertionsmeddelandet från fönstret Utdata till ett textfönster om du vill spara det för framtida referens. Utdatafönstret kan också innehålla andra felmeddelanden. Granska dessa meddelanden noggrant eftersom de ger ledtrådar till orsaken till kontrollfelet.
Använd försäkran för att identifiera fel under utvecklingen. Använd som regel en försäkran för varje antagande. Om du till exempel antar att ett argument inte är NULL använder du en försäkran för att testa det antagandet.
Kontroller i felsöknings- och versionsversioner
Påståendesatser kompileras endast om _DEBUG är definierad. Annars behandlar kompilatorn assertioner som null-satser. Därför medför försäkran inga kostnader eller prestandakostnader i det slutliga lanseringsprogrammet och gör att du kan undvika att använda #ifdef direktiv.
Biverkningar av att använda försäkran
När du lägger till försäkran i koden kontrollerar du att försäkran inte har biverkningar. Tänk till exempel på följande försäkran som ändrar nM värdet:
ASSERT(nM++ > 0); // Don't do this!
Eftersom uttrycket ASSERT inte utvärderas i versionen av ditt program har nM det olika värden i felsöknings- och versionsversionerna. Om du vill undvika det här problemet i MFC kan du använda VERIFY-makrot i stället ASSERTför .
VERIFY utvärderar uttrycket i alla andra versioner men kontrollerar inte resultatet i produktversionen.
Var särskilt noggrann med att använda funktionsanrop i påståendesatser, eftersom utvärdering av en funktion kan ha oväntade bieffekter.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY anropar myFnctn i både felsöknings- och versionsversionerna, så det är acceptabelt att använda. Att använda VERIFY medför dock omkostnaderna för ett onödigt funktionsanrop i releaseversionen.
CRT-uttryck
Headerfilen CRTDBG.H definierar makrona _ASSERT och _ASSERTE för kontroll av påståenden.
| Macro | Result |
|---|---|
_ASSERT |
Om det angivna uttrycket utvärderas till FALSE visas filnamnet och radnumret för _ASSERT. |
_ASSERTE |
Samma som _ASSERT, plus en strängrepresentation av uttrycket som har hävdats. |
_ASSERTE är mer kraftfull eftersom det rapporterar det påstående uttrycket som visade sig vara FALSK. Detta kan vara tillräckligt för att identifiera problemet utan att referera till källkoden. Felsökningsversionen av ditt program innehåller dock en strängkonstant för varje uttryck som hävdas med hjälp av _ASSERTE. Om du använder många _ASSERTE makron tar dessa stränguttryck upp en betydande mängd minne. Om det visar sig vara ett problem kan du använda _ASSERT för att spara minne.
När _DEBUG definieras, definieras _ASSERTE-makrot på följande sätt:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Om det bekräftade uttrycket utvärderas till FALSE anropas _CrtDbgReport för att rapportera kontrollfelet (med hjälp av en meddelandedialogruta som standard). Om du väljer Försök igen i meddelandedialogrutan _CrtDbgReport returnerar 1 och _CrtDbgBreak anropar felsökningsprogrammet via DebugBreak.
Om du tillfälligt behöver inaktivera alla kontroller, använder du _CtrSetReportMode.
Söker efter heap-korruption
I följande exempel används _CrtCheckMemory för att kontrollera om heapen är skadad:
_ASSERTE(_CrtCheckMemory());
Kontrollera pekarens giltighet
I följande exempel används _CrtIsValidPointer för att verifiera att ett visst minnesintervall är giltigt för läsning eller skrivning.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
I följande exempel används _CrtIsValidHeapPointer för att verifiera att en pekare pekar på minnet i den lokala högen (högen som skapats och hanteras av denna instans av C-körningsbiblioteket – en DLL kan ha sin egen instans av biblioteket och därmed sin egen hög, separat från applikationens hög). Den här försäkran fångar inte bara null- eller out-of-bounds-adresser, utan pekar även på statiska variabler, stackvariabler och annat icke-lokaliserat minne.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Kontrollera ett minnesblock
I följande exempel används _CrtIsMemoryBlock för att verifiera att ett minnesblock finns i den lokala heapen och har en giltig blocktyp.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
MFC-försäkran
MFC definierar ASSERT-makrot för kontrollkontroll. Den definierar också MFC ASSERT_VALID och CObject::AssertValid metoderna för att kontrollera det interna tillståndet för ett CObject-härlett objekt.
Om argumentet för MFC-makrot ASSERT utvärderas till noll eller falskt stoppar makrot programkörningen och varnar användaren. Annars fortsätter körningen.
När en försäkran misslyckas visas namnet på källfilen och radnumret för försäkran i en meddelandedialogruta. Om du väljer Försök igen i dialogrutan leder ett anrop till AfxDebugBreak till att körningen bryts till felsökningsprogrammet. Då kan du undersöka anropsstacken och använda andra felsökningsanläggningar för att avgöra varför försäkran misslyckades. Om du har aktiverat just-in-time-felsökning och felsökningsprogrammet inte redan kördes kan dialogrutan starta felsökningsprogrammet.
I följande exempel visas hur du använder ASSERT för att kontrollera returvärdet för en funktion:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Du kan använda ASSERT med funktionen IsKindOf för att ange typkontroll av funktionsargument:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
Makrot ASSERT genererar ingen kod i utgivningsversionen. Om du behöver utvärdera uttrycket i releaseversionen använder du makrot VERIFY i stället för ASSERT.
MFC ASSERT_VALID och CObject::AssertValid
Metoden CObject::AssertValid tillhandahåller körningskontroller av objektets interna tillstånd. Även om du inte behöver åsidosätta AssertValid när du härleder din klass från CObjectkan du göra klassen mer tillförlitlig genom att göra detta.
AssertValid ska utföra kontroller på alla objekts medlemsvariabler för att verifiera att de innehåller giltiga värden. Den bör till exempel kontrollera att pekarens medlemsvariabler inte är NULL.
I följande exempel visas hur du deklarerar en AssertValid funktion:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
När du åsidosätter AssertValidanropar du basklassversionen av AssertValid innan du utför dina egna kontroller. Använd sedan ASSERT-makrot för att kontrollera medlemmarna som är unika för din härledda klass, som du ser här:
#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
Om någon av dina medlemsvariabler lagrar objekt kan du använda makrot ASSERT_VALID för att testa deras interna giltighet (om deras klasser åsidosätter AssertValid).
Tänk dig till exempel en klass CMyData, som lagrar en CObList i en av dess medlemsvariabler. Variabeln CObList , m_DataList, lagrar en samling CPerson objekt. En förkortad förklaring av CMyData ser ut så här:
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 ...
};
Åsidosättningen AssertValid i CMyData ser ut så här:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData använder mekanismen AssertValid för att testa giltigheten för de objekt som lagras i dess datamedlem. Åsidosättande AssertValid av CMyData anropar makrot ASSERT_VALID för sin egen m_pDataList medlemsvariabel.
Validitetstestningen stoppas inte på den här nivån eftersom klassen CObList också åsidosätter AssertValid. Den här åsidosättningen utför ytterligare valideringstestning av listans interna tillstånd. Därför leder ett giltighetstest på ett CMyData objekt till ytterligare giltighetstester för det lagrade CObList listobjektets interna tillstånd.
Med lite mer arbete kan du lägga till giltighetstester för de CPerson objekt som lagras i listan också. Du kan härleda en klass CPersonList från CObList och åsidosätta AssertValid. I åsidosättningen anropar du CObject::AssertValid och itererar sedan genom listan för att anropa AssertValid på varje CPerson-objekt som är lagrat i listan. Klassen CPerson som visas i början av det här avsnittet har redan åsidosatt AssertValid.
Det här är en kraftfull mekanism när du skapar för felsökning. När du sedan skapar för lansering inaktiveras mekanismen automatiskt.
Begränsningar för AssertValid
Ett utlöst programuttryck indikerar att objektet klart är defekt och att körningen kommer att stoppas. En brist på försäkran tyder dock bara på att inga problem hittades, men objektet är inte garanterat bra.
Använda påståenden
Fånga logikfel
Du kan ange en försäkran på ett villkor som måste vara sant enligt logiken i ditt program. Försäkran har ingen effekt om inte ett logikfel inträffar.
Anta till exempel att du simulerar gasmolekyler i en container och att variabeln numMols representerar det totala antalet molekyler. Det här talet får inte vara mindre än noll, så du kan inkludera en MFC-försäkran som den här:
ASSERT(numMols >= 0);
Eller så kan du inkludera en CRT-försäkran som den här:
_ASSERT(numMols >= 0);
Dessa instruktioner gör ingenting om programmet fungerar korrekt. Om ett logikfel orsakar att numMols blir mindre än noll, avbryter dock kontrollen körningen av ditt program och visar dialogrutan Kontroll misslyckades.
Kontrollera resultat
Försäkran är värdefulla för testningsåtgärder vars resultat inte är uppenbara vid en snabb visuell inspektion.
Tänk till exempel på följande kod, som uppdaterar variabeln iMols baserat på innehållet i den länkade listan som pekas på av 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
Antalet molekyler som räknas av iMols måste alltid vara mindre än eller lika med det totala antalet molekyler, numMols. Visuell kontroll av loopen visar inte att detta nödvändigtvis kommer att vara fallet, så en försäkran används efter loopen för att testa för det villkoret.
Hitta ohanterade fel
Du kan använda kontrolluttryck för att testa felvillkor vid en punkt i koden där fel bör ha hanterats. I följande exempel returnerar en grafisk rutin en felkod eller noll för att lyckas.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Om felhanteringskoden fungerar korrekt ska felet hanteras och myErr återställas till noll innan försäkran nås. Om myErr har ett annat värde misslyckas försäkran, programmet stoppas och dialogrutan Försäkran misslyckades visas.
Påståendesatser är dock inte en ersättning för felhanteringskod. I följande exempel visas en försäkran som kan leda till problem i den slutliga versionskoden:
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Den här koden förlitar sig på försäkran för att hantera feltillståndet. Därför kommer alla felkoder som returneras av myGraphRoutine att bli ohanterade i den slutliga versionskoden.