Kontrolní výrazy jazyka C/C++
Příkaz kontrolního výrazu Určuje podmínku, kterou očekáváte, že bude platit v bodě programu. Pokud tato podmínka není pravdivá, kontrolní výraz se nezdařil, provádění programu je přerušeno a zobrazí se dialogové okno kontrolní výraz selhal .
Visual Studio podporuje příkazy kontrolního výrazu jazyka C++, které jsou založeny na následujících konstrukcích:
Kontrolní výrazy MFC pro programy MFC.
ATLASSERT pro programy, které používají ATL.
Kontrolní výrazy CRT pro programy, které používají knihovnu run-time jazyka C.
Funkce kontrolního výrazu ANSI pro ostatní programy C/C++.
Můžete použít kontrolní výrazy k zachycení logických chyb, kontrole výsledků operace a podmínek testování chyb, které by měly být zpracovány.
V tomto tématu
Kontrolní výrazy v sestaveních pro ladění a vydání
Vedlejší účinky použití kontrolních výrazů
Jak fungují kontrolní výrazy
Pokud se ladicí program zastaví z důvodu kontrolního výrazu běhové knihovny MFC nebo C, pak ladicí program přejde do bodu ve zdrojovém souboru, kde došlo k kontrolnímu výrazu. Zpráva kontrolního výrazu se zobrazí jak v okně výstup , tak v dialogovém okně kontrolního výrazu se nezdařilo . Můžete zkopírovat zprávu kontrolního výrazu z okna výstup do textového okna, pokud ho chcete uložit pro pozdější použití. Okno výstup může obsahovat také další chybové zprávy. Pečlivě zkontrolujte tyto zprávy, protože poskytují zprávy o příčině selhání kontrolního výrazu.
Použijte kontrolní výrazy k detekci chyb během vývoje. Jako pravidlo použijte jeden kontrolní výraz pro každý předpoklad. Například pokud předpokládáte, že argument není NULL, použijte k otestování tohoto předpokladu kontrolní výraz.
Kontrolní výrazy v sestaveních pro ladění a vydání
Příkazy kontrolního výrazu lze zkompilovat pouze v případě _DEBUG , že je definován. V opačném případě kompilátor považuje kontrolní výrazy za příkazy null. Proto příkazy kontrolního výrazu nedovolují žádné režijní náklady ani náklady na výkon v konečném programu pro vydávání verzí a umožňují vyhnout se #ifdef direktivám using.
Vedlejší účinky použití kontrolních výrazů
Pokud přidáte kontrolní výrazy do kódu, ujistěte se, že kontrolní výrazy nemají vedlejší účinky. Zvažte například následující výraz, který upraví nM hodnotu:
ASSERT(nM++ > 0); // Don't do this!
Vzhledem k tomu ASSERT , že výraz není vyhodnocen v prodejní verzi programu, nM budou mít v ladicích a vydaných verzích jiné hodnoty. Chcete-li se tomuto problému v knihovně MFC vyhnout, můžete použít makro ověřit místo ASSERT . VERIFY vyhodnotí výraz ve všech verzích, ale nevrátí výsledek ve vydané verzi.
Buďte obzvláště opatrní při použití volání funkcí v příkazech kontrolního výrazu, 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 obě verze ladění i verze, takže je přijatelné použít. Nicméně použití VERIFY působí režii zbytečného volání funkce ve vydané verzi.
Kontrolní výrazy CRT
SOUBORU Crtdbg. Soubor hlaviček H definuje makra _ASSERT a _ASSERTE pro kontrolu kontrolního výrazu.
| Podokně | Výsledek |
|---|---|
_ASSERT |
Pokud se zadaný výraz vyhodnotí jako FALSE, název souboru a číslo řádku _ASSERT . |
_ASSERTE |
Stejné jako _ASSERT , plus řetězcové vyjádření výrazu, který byl uplatněn. |
_ASSERTE je výkonnější, protože oznamuje výraz, který je vyměněn jako nepravdivý. To může být dostačující k identifikaci problému bez odkazování na zdrojový kód. Ladicí verze vaší aplikace však bude obsahovat řetězcovou konstantu pro každý výraz s kontrolními výrazy _ASSERTE . Pokud použijete mnoho _ASSERTE maker, tyto řetězcové výrazy zabírají značnou velikost paměti. Pokud se to ukáže jako problém, použijte _ASSERT k uložení paměti.
Když _DEBUG je definováno, _ASSERTE makro je definováno následujícím způsobem:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Pokud je výraz ASSERT vyhodnocen jako FALSE, je volána _CrtDbgReport k hlášení selhání kontrolního výrazu (pomocí dialogového okna zprávy ve výchozím nastavení). Pokud v dialogovém okně zpráva kliknete na tlačítko Opakovat , _CrtDbgReport vrátí hodnotu 1 a _CrtDbgBreak volání ladicího programu prostřednictvím DebugBreak .
Pokud potřebujete dočasně zakázat všechny kontrolní výrazy, použijte _CtrSetReportMode.
Kontroluje se poškození haldy.
Následující příklad používá _CrtCheckMemory ke kontrole poškození haldy:
_ASSERTE(_CrtCheckMemory());
Kontroluje se platnost ukazatele.
Následující příklad používá _CrtIsValidPointer k ověření, že daný rozsah paměti je 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 lokální haldě (halda vytvořená a spravovaná touto instancí knihovny run-time jazyka C – knihovna DLL může mít svou vlastní instanci knihovny, a proto vlastní haldu, mimo haldu aplikace). Tento kontrolní výraz zachytí pouze adresy null nebo mimo rozsah, ale také ukazatele na statické proměnné, proměnné zásobníku a jakoukoli jinou nemístní paměť.
_ASSERTE(_CrtIsValidPointer( myData );
Kontrola bloku paměti
Následující příklad používá _CrtIsMemoryBlock k ověření, zda je blok paměti v místní haldě a má platný typ bloku.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
Kontrolní výrazy MFC
MFC definuje makro kontrolního výrazu pro kontrolu kontrolního výrazu. Definuje také MFC ASSERT_VALID CObject::AssertValid metody a pro kontrolu vnitřního stavu CObject objektu odvozeného.
Pokud je argument ASSERT makra MFC vyhodnocen jako nula nebo false, makro zastaví spuštění programu a upozorní uživatele. v opačném případě pokračuje v provádění.
V případě neúspěchu kontrolního výrazu se zobrazí dialogové okno zpráva s názvem zdrojového souboru a číslem řádku kontrolního výrazu. Pokud v dialogovém okně kliknete na tlačítko Opakovat, volání AfxDebugBreak způsobí přerušení provádění do ladicího programu. V tomto okamžiku můžete prostudovat zásobník volání a použít další pomůcky ladicího programu k určení příčiny selhání kontrolního výrazu. Pokud jste povolili ladění za běhua ladicí program ještě nebyl spuštěn, dialogové okno může spustit ladicí program.
Následující příklad ukazuje, jak použít ASSERT ke kontrole návratové hodnoty funkce:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Můžete použít ASSERT s funkcí IsKindOf k poskytnutí kontroly typu argumentů funkce:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
ASSERTMakro negeneruje ve vydané verzi žádný kód. Pokud potřebujete vyhodnotit výraz ve vydané verzi, použijte místo výrazu ASSERT makro verify .
MFC ASSERT_VALID a CObject:: AssertValid
Metoda CObject:: AssertValid zajišťuje kontroly vnitřního stavu objektu v době běhu. I když při AssertValid odvozování třídy z nelze přepsat třídu CObject , je možné ji provést lépe spolehlivější. AssertValid by měla být provedena kontrolní výrazy pro všechny členské proměnné objektu pro ověření, že obsahují platné hodnoty. Například by měla kontrolovat, že proměnné členů ukazatele nejsou 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
// ...
};
Pokud přepíšete AssertValid , AssertValid před provedením vlastních kontrol volejte verzi základní třídy. Pak použijte makro ASSERT pro kontrolu členů, které jsou 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ý z vašich členských proměnných ukládá objekty, můžete použít ASSERT_VALID makro k otestování své vnitřní platnosti (pokud jejich třídy přepisují AssertValid ).
Zvažte například třídu CMyData , která ukládá CObList do jedné z jeho členských proměnných. CObListProměnná m_DataList ukládá kolekci CPerson objektů. Zkrácená deklarace vypadá takto CMyData :
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 ...
};
AssertValidPřepsání v CMyData takovém případě vypadá takto:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData používá AssertValid mechanismus k otestování platnosti objektů uložených ve svém datovém členu. Překrytí AssertValid CMyData vyvolá ASSERT_VALID makro pro vlastní m_pDataList členskou proměnnou.
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ší testování platnosti interního stavu seznamu. Proto test platnosti objektu vede k dalším testům platnosti interních stavů objektu CMyData CObList uloženého seznamu.
S větší námahotou můžete také přidat testy platnosti pro objekty uložené CPerson v seznamu. Třídu můžete odvodit z a CPersonList CObList přepsat AssertValid . V přepsání byste volali a pak iterují seznamem a volají se u každého CObject::AssertValid AssertValid objektu CPerson uloženého v seznamu. Třída CPerson zobrazená na začátku tohoto tématu už přepisuje AssertValid .
Jedná se o výkonný mechanismus při sestavování pro ladění. Když následně sestavíte pro vydání, mechanismus se automaticky vypne.
Omezení assertValid
Aktivované kontrolní výrazy indikuje, že objekt je rozhodně špatný a provádění se zastaví. Nedostatek kontrolního výrazu však pouze indikuje, že nebyl nalezen žádný problém, ale objekt není zaručený jako dobrý.
Použití kontrolních výrazů
Zachytání chyb logiky
Kontrolní výraz můžete nastavit pro podmínku, která musí být pravdivá podle logiky programu. Kontrolní výraz nemá žádný vliv, pokud se nevyskytne chyba logiky.
Předpokládejme například, že simulujete molekuly plynu v kontejneru a proměnná představuje numMols celkový počet molekul. Toto číslo nesmí být menší než nula, takže můžete zahrnout kontrolní příkaz MFC, jako je tento:
ASSERT(numMols >= 0);
Nebo můžete zahrnout kontrolní výraz CRT, jako je tento:
_ASSERT(numMols >= 0);
Tyto příkazy nedělat nic, pokud váš program pracuje správně. Pokud ale chyba logiky způsobí, že bude menší než nula, kontrolní výraz zastaví provádění programu a zobrazí dialogové okno numMols Kontrolní výraz se nezdařil.
Kontrola výsledků
Kontrolní výrazy jsou užitečné pro testovací operace, jejichž výsledky nejsou z rychlé vizuální kontroly zřejmé.
Představte si například následující kód, který aktualizuje proměnnou na základě obsahu propojeného seznamu, iMols na který odkazuje 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
Počet molekul spočítaných podle musí být vždy menší nebo roven celkovému počtu molekul iMols numMols . Vizuální kontrola smyčky neukašuje, že by tomu tak bylo, takže se po smyčce použije kontrolní příkaz k otestování této podmínky.
Hledání neošetřených chyb
Kontrolní výrazy můžete použít k otestování chybových podmínek v bodě v kódu, kde by měly být zpracovány jakékoli chyby. V následujícím příkladu grafická rutina vrátí 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 se chyba před dosažením kontrolního výrazu zpracovat a myErr resetovat na nulu. Pokud má jinou hodnotu, kontrolní výraz selže, program se zastaví a zobrazí se dialogové okno Kontrolní výraz myErr se nezdařil.
Kontrolní příkazy však nenahrazuje kód pro zpracování chyb. Následující příklad ukazuje kontrolní příkaz, který může vést k problémům v konečném kódu 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 využívá příkaz kontrolního výrazu ke zpracování chybového stavu. V důsledku toho bude kód chyby vrácený v kódu konečné verze myGraphRoutine neošetřen.