Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az állítási utasítás olyan feltételt határoz meg, amely a program egy pontján való igaznak számít. Ha ez a feltétel nem igaz, az állítás meghiúsul, a program végrehajtása megszakad, és megjelenik a Helyességi sikertelenség párbeszédpanel .
A Visual Studio az alábbi szerkezeteken alapuló C++ állítási utasításokat támogatja:
MFC-állítások MFC-programokhoz.
ATLASSERT atl-t használó programokhoz.
CRT-állítások a C futásidejű kódtárat használó programokhoz.
Az ANSI-érvényesítési függvény más C/C++ programokhoz.
Az állításokkal logikai hibákat foghat el, ellenőrizheti egy művelet eredményeit, és tesztelheti a hibafeltételeket, amelyeket kezelni kellett volna.
Ebben a témakörben
Állítások hibakeresési és kiadási buildekben
Az állítások használatának mellékhatásai
Az állítások működése
Ha a hibakereső egy MFC vagy C futásidejű kódtár-állítás miatt leáll, akkor ha a forrás elérhető, a hibakereső a forrásfájl azon pontjára navigál, ahol az állítás történt. Az érvényesítési üzenet a Kimenet ablakban és az Igény nem sikerült párbeszédpanelen is megjelenik. A helyességi üzenetet a Kimenet ablakból átmásolhatja egy szövegablakba, ha későbbi használatra szeretné menteni. A Kimeneti ablak más hibaüzeneteket is tartalmazhat. Gondosan vizsgálja meg ezeket az üzeneteket, mert nyomokat adnak az állítási hiba okához.
A fejlesztés során hibák észleléséhez használjon állításokat. Általában minden feltételezéshez használjon egy állítást. Ha például azt feltételezi, hogy egy argumentum nem NULL, egy állítás használatával tesztelje ezt a feltételezést.
Állítások hibakeresési és kiadási buildekben
A helyességi utasítások csak akkor állnak össze, ha _DEBUG definiálva van. Ellenkező esetben a fordító üres utasításként kezeli az állításokat. Ezért az állítási utasítások nem rónak többletterhelést vagy teljesítményköltséget a végleges kiadási programra, és lehetővé teszik, hogy elkerülhetjük a #ifdef direktívák használatát.
Az állítások használatának mellékhatásai
Ha állításokat ad hozzá a kódhoz, győződjön meg arról, hogy az állításoknak nincsenek mellékhatásai. Vegyük például az alábbi állítást, amely módosítja az nM értéket:
ASSERT(nM++ > 0); // Don't do this!
Mivel a ASSERT kifejezés nem a program kiadási verziójában van kiértékelve, nM a hibakeresési és kiadási verziókban eltérő értékek jelennek meg. A probléma elkerülése érdekében az MFC-ben a VERIFY makrót használhatja ahelyett ASSERT, hogy .
VERIFY kiértékeli a kifejezést az összes verzióban, de nem ellenőrzi az eredményt a kiadási verzióban.
Különösen ügyeljen arra, hogy függvényhívásokat használjon az állítási utasításokban, mert a függvények kiértékelése váratlan mellékhatásokat okozhat.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY mind a Debug, mind a Release verzióban meghívja myFnctn, ezért elfogadható a használata. Azonban, a VERIFY használata a kiadási verzióban szükségtelen függvényhívásból származó többletterhet okoz.
CRT-állítások
A CRTDBG.H fejlécfájl határozza meg a helyességi ellenőrzéshez szükséges makrókat és _ASSERT makrókat._ASSERTE
| Macro | Result |
|---|---|
_ASSERT |
Ha a megadott kifejezés értéke HAMIS, akkor a fájl neve és sorszáma megjelenik._ASSERT |
_ASSERTE |
Ugyanaz, mint _ASSERT, valamint az érvényesített kifejezés szövegábrázolása. |
_ASSERTE hatékonyabb, mert beszámol azon állított kifejezésről, amely hamisnak bizonyult. Ez elegendő lehet a probléma azonosításához a forráskódra való hivatkozás nélkül. Az alkalmazás Debug verziója azonban egy szövegkonstansot fog tartalmazni minden olyan kifejezéshez, amelyet kijelentettünk _ASSERTE használatával. Ha sok _ASSERTE makrót használ, ezek a sztringkifejezések jelentős mennyiségű memóriát vesznek igénybe. Ha ez problémának bizonyul, használja _ASSERT a memória mentéséhez.
Ha _DEBUG definiálva van, a makró a _ASSERTE következőképpen van definiálva:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Ha az érvényesített kifejezés HAMIS értékre van kiértékelve, a rendszer meghívja _CrtDbgReport az állítási hiba jelentésére (alapértelmezés szerint egy üzenet párbeszédpanel használatával). Ha az Üzenet párbeszédpanelen az Újrapróbálkozás lehetőséget választja, a c1 /> 1-et ad vissza, és a _CrtDbgBreak meghívja a hibakeresőt a DebugBreak-en keresztül.
Ha ideiglenesen le kell tiltania az összes állítást, használja _CtrSetReportMode.
A halom sérülésének ellenőrzése
Az alábbi példa a _CrtCheckMemory-t használja a halom sérülésének ellenőrzésére:
_ASSERTE(_CrtCheckMemory());
Mutató érvényességének ellenőrzése
Az alábbi példa _CrtIsValidPointer használatával ellenőrzi, hogy egy adott memóriatartomány érvényes-e olvasásra vagy írásra.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
Az alábbi példa _CrtIsValidHeapPointer használ annak ellenőrzésére, hogy egy mutató a helyi halomban lévő memóriára mutat-e (a C futásidejű kódtár ezen példánya által létrehozott és felügyelt halom – a DLL-nek saját példánya lehet a tárból, és így saját halomból, az alkalmazás halomtárán kívül). Ez az állítás nem csak a null vagy a határon kívüli címeket rögzíti, hanem statikus változókra, veremváltozókra és egyéb nem lokális memóriára mutató mutatókat is.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Memóriablokk ellenőrzése
Az alábbi példa _CrtIsMemoryBlock használatával ellenőrzi, hogy egy memóriablokk a helyi halomban van-e, és érvényes blokktípussal rendelkezik-e.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
MFC-állítások
Az MFC határozza meg az ASSERT makrót a kijelentés-ellenőrzéshez. Meghatározza a MFC ASSERT_VALID származtatott objektumok belső állapotának CObject::AssertValidellenőrzésére szolgáló módszereket és CObject módszereket is.
Ha az MFC-makró ASSERT argumentuma nullára vagy hamisra értékel, a makró leállítja a program végrehajtását, és értesíti a felhasználót; ellenkező esetben a végrehajtás folytatódik.
Ha egy állítás meghiúsul, egy üzenet párbeszédpanelen megjelenik a forrásfájl neve és az állítás sorszáma. Ha a párbeszédpanelen az Újrapróbálkozás lehetőséget választja, az AfxDebugBreak hívása miatt a végrehajtás megszakad a hibakeresőhöz. Ezen a ponton megvizsgálhatja a hívási vermet, és más hibakereső létesítmények használatával megállapíthatja, hogy az állítás miért nem sikerült. Ha engedélyezte a just-in-time hibakeresést, és a hibakereső még nem futott, a párbeszédpanel elindíthatja a hibakeresőt.
Az alábbi példa bemutatja, hogyan ellenőrizheti ASSERT egy függvény visszatérési értékét:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
A IsKindOf függvényt az ASSERT-tel használhatja, hogy típusellenőrzést végezzen a függvényargumentumok között.
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
A ASSERT makró nem hoz létre kódot a Kiadás verzióban. Ha kiértékelni kell a kifejezést a kiadás verzióban, használja a VERIFY makrót az ASSERT helyett.
MFC ASSERT_VALID és CObject::AssertValid
A CObject::AssertValid metódus futásidejű ellenőrzéseket biztosít egy objektum belső állapotáról. Bár nem kötelező felülírnia a CObject osztály származékát, ezzel a módszerrel megbízhatóbbá teheti az osztályt.
AssertValid az objektum összes tagváltozóján ellenőrzéseket kell végrehajtani, hogy meggyőződjünk arról, hogy érvényes értékeket tartalmaznak. Ellenőriznie kell például, hogy a mutatótag változói nem NULL értékűek-e.
Az alábbi példa bemutatja, hogyan deklarálhat függvényt AssertValid :
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
A felülbíráláskor AssertValidhívja meg az alaposztály verzióját AssertValid , mielőtt saját ellenőrzéseket végez. Ezután az ASSERT makróval ellenőrizze a származtatott osztályhoz tartozó egyedi tagokat az itt látható módon:
#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
Ha bármelyik tagváltozója objektumokat tárol, a ASSERT_VALID makrót használhatja az objektumok belső érvényességének tesztelésére (feltéve, hogy az osztályaik felülírják a AssertValid metódust).
Vegyük például azt az osztályt CMyData, amely egy CObList-et tárol az egyik tagváltozójában. A CObList változó m_DataListobjektumgyűjteményt CPerson tárol. A következőképpen néz ki egy rövidített deklaráció 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 ...
};
A AssertValid felülbírálás a CMyData következőképpen néz ki:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData a mechanizmus használatával AssertValid teszteli az adatelemben tárolt objektumok érvényességét. A CMyData felülírása a ASSERT_VALID makró meghívását követeli meg a saját m_pDataList tagváltozójához.
Az érvényességi tesztelés ezen a szinten nem áll le, mert az osztály CObList felülbírálja AssertValidis. Ez a felülbírálás további érvényességi teszteket végez a lista belső állapotán. Így egy CMyData objektum érvényességi tesztje további érvényességi teszteket eredményez a tárolt CObList listaobjektum belső állapotára vonatkozóan.
Néhány további munkával érvényességi teszteket is hozzáadhat a CPerson listában tárolt objektumokhoz. Származtathat egy CPersonList osztályt a CObList-ből, és felülbírálhatja a AssertValid-t. A felülbírálás során meghívja CObject::AssertValid, majd végigiterál a listán, és meghívja a AssertValid metódust a listában tárolt minden egyes CPerson objektumra. A CPerson témakör elején látható osztály már felülírja AssertValid.
Ez egy hatékony mechanizmus, ha hibakereséshez épít. Amikor később kiadásra készül, a rendszer automatikusan kikapcsolja a mechanizmust.
Az AssertValid korlátozásai
Az aktivált állítás azt jelzi, hogy az objektum határozottan rossz, és a végrehajtás leáll. Az állítás hiánya azonban csak azt jelzi, hogy nem található probléma, de az objektum nem garantáltan jó.
Állítások használata
Logikai hibák észlelése
Beállíthat olyan feltételre vonatkozó állítást, amely a program logikájának megfelelően igaznak kell lennie. Az állításnak nincs hatása, hacsak nem fordul elő logikai hiba.
Tegyük fel például, hogy gázmolekulákat szimulál egy tárolóban, és a változó numMols a molekulák teljes számát jelöli. Ez a szám nem lehet kisebb nullánál, ezért az alábbihoz hasonló MFC-utasítást is tartalmazhat:
ASSERT(numMols >= 0);
Vagy a következőhöz hasonló CRT-állítást is tartalmazhat:
_ASSERT(numMols >= 0);
Ezek az utasítások nem tesznek semmit, ha a program megfelelően működik. Ha azonban egy logikai hiba nullánál kisebb hibát okoz numMols , az állítás leállítja a program végrehajtását, és megjeleníti a Hibás állítás párbeszédpanelt.
Eredmények ellenőrzése
Az állítások értékesek olyan tesztelési műveletekhez, amelyek eredményei nem egyértelműek egy gyors vizualizációs vizsgálatból.
Vegyük például a következő kódot, amely a változót iMols a hivatkozott lista molstartalma alapján frissíti:
/* 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
A megszámlált iMols molekulák számának mindig kisebbnek vagy egyenlőnek kell lennie a molekulák teljes számánál, numMols. A hurok vizuális vizsgálata nem mutatja, hogy ez feltétlenül így lesz, ezért a ciklus után egy helyességi utasítást használnak az adott feltétel teszteléséhez.
Nem kezelt hibák keresése
Állításokkal tesztelheti a hibahelyzeteket a kód azon pontján, ahol minden hibát kezelni kellett volna. Az alábbi példában egy grafikus rutin egy hibakódot vagy nullát ad vissza a sikerhez.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Ha a hibakezelő kód megfelelően működik, a hibát kezelni kell, és myErr nullára kell állítani az állítás elérése előtt. Ha a myErr más értéket vesz fel, az állítás meghiúsul, a program leáll, és megjelenik az Állítás Sikertelen Párbeszédpanel.
A helyességi utasítások azonban nem helyettesítik a hibakezelési kódot. Az alábbi példa egy állítási utasítást mutat be, amely problémákhoz vezethet a végleges kiadási kódban:
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Ez a kód a helyességi utasításra támaszkodik a hibafeltétel kezeléséhez. Ennek eredményeképpen a visszaadott myGraphRoutine hibakódok nem lesznek kezelve a végleges kiadási kódban.