Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Příkaz asertivního výrazu určuje podmínku, kterou očekáváte, že bude pravdivá v určitém bodě programu. Pokud tato podmínka není pravdivá, kontrolní výraz selže, spuštění programu se přeruší a zobrazí se dialogové okno Kontrolní výraz se nezdařilo .
Visual Studio podporuje příkazy kontrolních výrazů jazyka C++, které jsou založené na následujících konstruktorech:
Kontrolní výrazy MFC pro programy MFC.
ATLASSERT pro programy, které používají ATL.
Aserce CRT pro programy, které používají běhovou knihovnu jazyka C.
Funkce ASSERT ANSI pro ostatní programy C/C++.
Kontrolní výrazy můžete použít k zachycení chyb logiky, kontrole výsledků operace a testovacích chybových podmínek, které by měly být zpracovány.
V tomto tématu
Kontrolní výrazy v buildech ladění a vydaných verzí
Vedlejší účinky použití asercí
Jak fungují kontrolní výrazy
Když se ladicí program zastaví kvůli aserci knihovny MFC nebo C za běhu, a pokud je zdrojový soubor dostupný, ladicí program přejde do bodu ve zdrojovém souboru, kde k aserci došlo. Zpráva kontrolního výrazu se zobrazí v okně výstupu i v dialogovém okně Chyba kontrolního výrazu. Chcete-li zprávu kontrolního výrazu uložit pro budoucí použití, můžete ji zkopírovat z okna Výstup do textového okna. Okno Výstup může obsahovat i další chybové zprávy. Tyto zprávy pečlivě prozkoumejte, protože poskytují vodítka k příčině selhání tvrzení.
Pomocí asercí můžete detekovat chyby během vývoje. Jako pravidlo použijte pro každý předpoklad jeden kontrolní výraz. Pokud například předpokládáte, že argument není NULL, použijte k otestování daného předpokladu kontrolní výraz.
Kontrolní výrazy v buildech ladění a vydaných verzí
Asertní příkazy se kompilují pouze v případě, že _DEBUG je definován. V opačném případě kompilátor zachází s kontrolními výrazy jako s příkazy null. Asertivní příkazy proto v konečném programu ve verzi Release neukládají žádné režijní náklady ani náklady na výkon a umožňují se vyhnout direktivám using #ifdef.
Vedlejší účinky použití asercí
Když do kódu přidáte kontrolní výrazy, ujistěte se, že kontrolní výrazy nemají vedlejší účinky. Představte si například následující podmínku, která upraví hodnotu nM:
ASSERT(nM++ > 0); // Don't do this!
Vzhledem k tomu, že výraz ASSERT není vyhodnocen ve verzi vydané verze programu, nM budou mít různé hodnoty ve verzích Debug a Release. Chcete-li se tomuto problému vyhnout v prostředí MFC, můžete místo ASSERT použít makro VERIFY.
VERIFY vyhodnotí výraz ve všech verzích, ale nekontroluje výsledek v Release verzi.
Dávejte pozor na používání volání funkcí v asertních příkazech, protože vyhodnocení funkce může mít neočekávané vedlejší účinky.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY volá myFnctn jak ve verzích Debug, tak Release, takže je přijatelné jej použít. Použití VERIFY však představuje režii nepotřebného volání funkce ve vydané verzi.
Kontrolní výrazy CRT
Hlavičkový soubor CRTDBG.H definuje makra _ASSERT a _ASSERTE pro kontrolu výrazů pomocí asercí.
| Macro | Result |
|---|---|
_ASSERT |
Pokud se zadaný výraz vyhodnotí jako NEPRAVDA, zobrazí se název souboru a číslo řádku _ASSERT. |
_ASSERTE |
Je totožné s _ASSERT, plus řetězcové vyjádření výrazu, který byl asertován. |
_ASSERTE je výkonnější, protože hlásí vyhodnocený výraz podmínky, který se ukázal jako nepravdivý. To může stačit k identifikaci problému bez odkazu na zdrojový kód. Ladicí verze vaší aplikace však bude obsahovat řetězcovou konstantu pro každý výraz, který je ověřen pomocí _ASSERTE. Pokud používáte mnoho makra _ASSERTE, tyto řetězcové výrazy zabírají značné množství paměti. Pokud se to ukáže jako problém, použijte _ASSERT k uložení paměti.
Když je definováno _DEBUG, makro _ASSERTE je definováno takto:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Pokud se výraz asertovaný vyhodnotí jako FALSE, je volána funkce _CrtDbgReport k hlášení selhání tvrzení (ve výchozím nastavení pomocí dialogového okna zprávy). Pokud v dialogovém okně zprávy zvolíte Možnost Opakovat , _CrtDbgReport vrátí hodnotu 1 a _CrtDbgBreak zavolá ladicí program prostřednictvím DebugBreak.
Pokud potřebujete dočasně zakázat všechny aserty, použijte _CtrSetReportMode.
Kontrola poškození haldy
Následující příklad používá funkci _CrtCheckMemory ke kontrole, zda nedošlo k poškození haldy.
_ASSERTE(_CrtCheckMemory());
Kontrola platnosti ukazatele
Následující příklad používá _CrtIsValidPointer k ověření, zda je daná oblast paměti platná pro čtení nebo zápis.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
Následující příklad používá _CrtIsValidHeapPointer k ověření ukazatele na paměť v místní haldě (halda vytvořená a spravovaná touto instancí knihovny runtime jazyka C – knihovna DLL může mít svou vlastní instanci knihovny, a proto vlastní haldu mimo haldu aplikace). Tento výraz zachytává nejen nulové nebo mimohraniční adresy, ale také ukazatele na statické proměnné, proměnné zásobníku a jakoukoli jinou nelokální paměť.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Kontrola bloku paměti
Následující příklad používá _CrtIsMemoryBlock k ověření, že blok paměti je v místní haldě a má platný typ bloku.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
MFC – kontrolní výrazy
MFC definuje makro ASSERT pro kontrolu výrazů. Definuje také MFC ASSERT_VALID a CObject::AssertValid metody pro kontrolu vnitřního stavu -odvozeného objektu CObject.
Pokud se argument makra MFC ASSERT vyhodnotí jako nula nebo nepravda, makro zastaví provádění programu a upozorní uživatele. V opačném případě bude provádění pokračovat.
Pokud kontrolní výraz selže, zobrazí se v dialogovém okně se zprávou název zdrojového souboru a číslo řádku kontrolního výrazu. Pokud v dialogovém okně zvolíte Opakovat, volání AfxDebugBreak způsobí zastavení provádění pro ladicí program. V tomto okamžiku můžete prozkoumat zásobník volání a pomocí dalších funkcí ladicího programu zjistit, proč asercí selhala. Pokud jste povolili ladění za běhu a ladicí program ještě nebyl spuštěný, dialogové okno může spustit ladicí program.
Následující příklad ukazuje, jak zkontrolovat ASSERT návratovou hodnotu funkce:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Funkci ASSERT s funkcí IsKindOf můžete použít k zajištění kontroly typů argumentů funkce:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
Makro ASSERT nevygeneruje žádný kód ve vydané verzi. Pokud potřebujete výraz vyhodnotit ve vydané verzi, použijte místo ASSERT makro VERIFY.
MFC ASSERT_VALID a CObject::AssertValid
CObject::AssertValid metoda poskytuje kontroly za běhu interního stavu objektu. I když není nutné přepisovat AssertValid, když odvozujete svou třídu z CObject, můžete tím svoji třídu učinit spolehlivější.
AssertValid by měl provádět asercní kontroly pro všechny členské proměnné objektu, aby bylo možné ověřit, zda obsahují platné hodnoty. Měla by například zkontrolovat, že členské proměnné ukazatele nemají hodnotu NULL.
Následující příklad ukazuje, jak deklarovat AssertValid funkci:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
**
Při přepsání AssertValid volejte verzi AssertValid základní třídy před provedením vlastních kontrol. Pak pomocí makra ASSERT zkontrolujte členy jedinečné pro vaši odvozenou třídu, jak je znázorněno zde:
#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
Pokud některé vaše členské proměnné ukládají objekty, můžete použít makro ASSERT_VALID k otestování jejich vnitřní platnosti (pokud jejich třídy přepisují AssertValid).
Představte si například třídu CMyData, která ukládá CObList v jedné z jejích členských proměnných. Proměnná CObList , m_DataListukládá kolekci CPerson objektů. Zkrácená deklarace CMyData vypadá takto:
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 ...
};
Přepsání AssertValid v CMyData vypadá takto:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData
AssertValid používá mechanismus k otestování platnosti objektů uložených ve svém datovém členu. Přepsání AssertValidCMyData vyvolá ASSERT_VALID makro pro vlastní členskou proměnnou m_pDataList.
Testování platnosti se na této úrovni nezastaví, protože třída CObList také přepisuje AssertValid. Toto přepsání provádí další kontrolu platnosti stavu uvnitř seznamu. Proto test platnosti objektu CMyData vede k dalším testům platnosti pro vnitřní stavy uloženého CObList objektu seznamu.
S další prací můžete také přidat testy platnosti pro CPerson objekty uložené v seznamu. Můžete odvodit třídu od CPersonList od CObList a přepsat AssertValid. V přepsání byste zavolali CObject::AssertValid, a poté byste procházeli seznam a volali AssertValid na každý CPerson objekt uložený v seznamu. Třída CPerson zobrazená na začátku tohoto tématu již přepisuje AssertValid.
Jedná se o výkonný mechanismus při sestavování pro ladění. Při následném sestavení pro vydání se mechanismus automaticky vypne.
Omezení AssertValid
Aktivovaná aserce označuje, že objekt je rozhodně chybný a spuštění se nezdaří. Nedostatek aserce však značí pouze, že nebyl nalezen žádný problém, ale objekt není zaručeno, že je v pořádku.
Použití asercí
Zachycení chyb logiky
Aserci můžete nastavit na podmínku, která musí odpovídat logice vašeho programu. Aserce nemá žádný vliv, pokud nedojde k logické chybě.
Předpokládejme například, že simulujete molekuly plynu v kontejneru a proměnná numMols představuje celkový počet molekul. Toto číslo nemůže být menší než nula, proto můžete zahrnout kontrolní příkaz MFC, například takto:
ASSERT(numMols >= 0);
Nebo můžete zahrnout CRT asercion takto:
_ASSERT(numMols >= 0);
Tyto příkazy nedělají nic, pokud váš program funguje správně. Pokud logická chyba způsobí, že numMols je menší než nula, asertivní výrok zastaví provádění programu a zobrazí se dialogové okno Selhání asertivního výroku.
Kontrola výsledků
Aserce jsou cenné pro testovací operace, jejichž výsledky nejsou zřejmé z rychlé vizuální inspekce.
Představte si například následující kód, který aktualizuje proměnnou iMols na základě obsahu propojeného seznamu, na který molsodkazuje:
/* 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
Počet molekul počítaných iMols podle musí být vždy menší než nebo roven celkovému počtu molekul, numMols. Vizuální kontrola smyčky nenaznačuje, že by to nutně měl být tento případ, takže po smyčce se použije asertační příkaz k otestování této podmínky.
Hledání neošetřených chyb
Aserce můžete použít k testování chybových podmínek v místě v kódu, kde měly být jakékoliv chyby už zpracovány. V následujícím příkladu vrátí grafická rutina kód chyby nebo nulu pro úspěch.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Pokud kód pro zpracování chyb funguje správně, měla by být chyba zpracována a myErr by měl být před dosažením kontrolního výrazu resetován na nulu. Pokud myErr má jinou hodnotu, kontrolní výraz selže, program se zastaví a zobrazí se dialogové okno Kontrolní výraz se nezdařilo .
Tvrzovací příkazy však nejsou náhradou za kód pro zpracování chyb. Následující příklad ukazuje příkaz assert, který může vést k problémům v konečném kódu finální verze.
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Tento kód spoléhá na tvrzení pro zpracování chybového stavu. V důsledku toho nebude žádný chybový kód vrácený myGraphRoutine ve finální verzi kódu ošetřen.