Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
Une instruction d’assertion spécifie une condition que vous prévoyez d’être vraie à un moment dans votre programme. Si cette condition n’est pas vraie, l’assertion échoue, l’exécution de votre programme est interrompue et la boîte de dialogue Assertion Failed s’affiche.
Visual Studio prend en charge les instructions d’assertion C++ basées sur les constructions suivantes :
Assertions MFC pour les programmes MFC.
ATLASSERT pour les programmes qui utilisent ATL.
Assertions CRT pour les programmes qui utilisent la bibliothèque d’exécution C.
Fonction d’assertion ANSI pour d’autres programmes C/C++.
Vous pouvez utiliser des assertions pour intercepter les erreurs logiques, vérifier les résultats d’une opération et tester les conditions d’erreur qui doivent avoir été gérées.
Dans cette rubrique
Assertions dans les builds Debug et Release
Effets secondaires de l’utilisation d’assertions
Fonctionnement des assertions
Lorsque le débogueur s’arrête en raison d’une assertion de bibliothèque runtime MFC ou C, si la source est disponible, le débogueur accède au point dans le fichier source où l’assertion s’est produite. Le message d’assertion apparaît à la fois dans la fenêtre Sortie et dans la boîte de dialogue Échec de l’assertion . Vous pouvez copier le message d’assertion de la fenêtre Sortie vers une fenêtre de texte si vous souhaitez l’enregistrer pour une référence ultérieure. La fenêtre Sortie peut également contenir d’autres messages d’erreur. Examinez attentivement ces messages, car ils fournissent des indices sur la cause de l’échec de l’assertion.
Utilisez des assertions pour détecter les erreurs pendant le développement. En règle générale, utilisez une assertion pour chaque hypothèse. Par exemple, si vous supposez qu’un argument n’est pas NULL, utilisez une assertion pour tester cette hypothèse.
Assertions dans les builds Debug et Release
Les instructions d'assertion sont compilées uniquement si _DEBUG est défini. Sinon, le compilateur traite les assertions comme des instructions Null. Par conséquent, les instructions d’assertion n’imposent aucune surcharge ou coût de performances dans votre programme de mise en production final et vous permettent d’éviter d’utiliser des instructions #ifdef.
Effets secondaires de l’utilisation d’assertions
Lorsque vous ajoutez des assertions à votre code, assurez-vous que les assertions n’ont pas d’effets secondaires. Par exemple, considérez l’assertion suivante qui modifie la nM valeur :
ASSERT(nM++ > 0); // Don't do this!
Étant donné que l’expression ASSERT n’est pas évaluée dans la version Release de votre programme, nM elle a des valeurs différentes dans les versions Debug et Release. Pour éviter ce problème dans MFC, vous pouvez utiliser la macro VERIFY au lieu de ASSERT.
VERIFY évalue l’expression dans toutes les versions, mais ne vérifie pas le résultat dans la version Release.
Soyez particulièrement prudent lors de l'utilisation des appels de fonctions dans les instructions d’assertion, car l’évaluation d’une fonction peut avoir des effets secondaires inattendus.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY appelle myFnctn dans les versions de débogage et de mise en production, donc il est acceptable de l'utiliser. Toutefois, l’utilisation VERIFY impose la surcharge d’un appel de fonction inutile dans la version Release.
Assertions du CRT
Le CRTDBG.H fichier d’en-tête définit les macros _ASSERT et _ASSERTE pour la vérification des assertions.
| Macro | Résultat |
|---|---|
_ASSERT |
Si l’expression spécifiée prend la valeur FALSE, le nom de fichier et le numéro de ligne du _ASSERTfichier . |
_ASSERTE |
Identique à _ASSERT, ainsi qu’une représentation sous forme de chaîne de l’expression qui a été affirmée. |
_ASSERTE est plus puissant, car il signale l’expression déclarée qui s’est avérée être FALSE. Cela peut suffire pour identifier le problème sans faire référence au code source. Toutefois, la version de débogage de votre application contient une constante de chaîne pour chaque expression assertée à l’aide de _ASSERTE. Si vous utilisez de nombreuses _ASSERTE macros, ces expressions de chaîne prennent une quantité significative de mémoire. Si cela prouve qu’il s’agit d’un problème, utilisez-le _ASSERT pour économiser de la mémoire.
Quand _DEBUG est définie, la macro _ASSERTE est définie comme suit :
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Si l’expression déclarée a la valeur FALSE, _CrtDbgReport est appelée pour signaler l’échec d’assertion (à l’aide d’une boîte de dialogue de message par défaut). Si vous choisissez Réessayer dans la boîte de dialogue message, _CrtDbgReport retourne 1 et _CrtDbgBreak appelle le débogueur via DebugBreak.
Si vous devez désactiver temporairement toutes les assertions, utilisez _CtrSetReportMode.
Vérification de l’altération de la mémoire heap
L’exemple suivant utilise _CrtCheckMemory pour vérifier l’altération du tas :
_ASSERTE(_CrtCheckMemory());
Vérification de la validité du pointeur
L’exemple suivant utilise _CrtIsValidPointer pour vérifier qu’une plage de mémoire donnée est valide pour la lecture ou l’écriture.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
L’exemple suivant utilise _CrtIsValidHeapPointer pour vérifier qu’un pointeur pointe vers la mémoire dans le tas local (le tas créé et géré par cette instance de la bibliothèque runtime C , une DLL peut avoir sa propre instance de la bibliothèque, et donc son propre tas, en dehors du tas de l’application). Cette assertion intercepte non seulement les adresses null ou hors limites, mais également les pointeurs vers des variables statiques, des variables de pile et toute autre mémoire non locale.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Vérification d’un bloc de mémoire
L’exemple suivant utilise _CrtIsMemoryBlock pour vérifier qu’un bloc de mémoire se trouve dans le tas local et a un type de bloc valide.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
Assertions MFC
MFC définit la macro ASSERT pour la vérification des assertions. Il définit également les méthodes MFC ASSERT_VALID et CObject::AssertValid pour vérifier l'état interne d’un objet dérivé CObject.
Si l’argument de la macro MFC ASSERT prend la valeur zéro ou false, la macro arrête l’exécution du programme et alerte l’utilisateur ; sinon, l’exécution se poursuit.
Lorsqu’une assertion échoue, une boîte de dialogue de message affiche le nom du fichier source et le numéro de ligne de l’assertion. Si vous choisissez Réessayer dans la boîte de dialogue, un appel à AfxDebugBreak entraîne l’arrêt de l’exécution vers le débogueur. À ce stade, vous pouvez examiner la pile des appels et utiliser d’autres outils de débogage pour déterminer pourquoi l’assertion a échoué. Si vous avez activé le débogage juste-à-temps et que le débogueur n’était pas déjà en cours d’exécution, la boîte de dialogue peut lancer le débogueur.
L’exemple suivant montre comment vérifier ASSERT la valeur de retour d’une fonction :
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Vous pouvez utiliser ASSERT avec la fonction IsKindOf pour fournir la vérification de type des arguments de fonction :
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
La ASSERT macro ne produit aucun code dans la version release. Si vous devez évaluer l’expression dans la version Release, utilisez la macro VERIFY au lieu de ASSERT.
MFC ASSERT_VALID et CObject ::AssertValid
La méthode CObject ::AssertValid fournit des vérifications au moment de l’exécution de l’état interne d’un objet. Bien que vous ne soyez pas obligé de remplacer AssertValid lorsque vous dérivez votre classe CObject, vous pouvez rendre votre classe plus fiable en procédant ainsi.
AssertValid doit effectuer des assertions sur toutes les variables membres de l’objet pour vérifier qu’elles contiennent des valeurs valides. Par exemple, il doit vérifier que les variables membres du pointeur ne sont pas NULL.
L’exemple suivant montre comment déclarer une AssertValid fonction :
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
Lorsque vous remplacez AssertValid, appelez la version de classe de base de AssertValid avant d’effectuer vos propres vérifications. Utilisez ensuite la macro ASSERT pour vérifier les membres propres à votre classe dérivée, comme illustré ici :
#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
Si l’une de vos variables membres stocke des objets, vous pouvez utiliser la ASSERT_VALID macro pour tester leur validité interne (si leurs classes remplacent AssertValid).
Par exemple, considérez une classe CMyData, qui stocke un CObList dans l’une de ses variables membres. La CObList variable, , m_DataListstocke une collection d’objets CPerson . Une déclaration abrégée CMyData ressemble à ceci :
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 ...
};
La surcharge AssertValid dans CMyData ressemble à ceci :
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData utilise le AssertValid mécanisme pour tester la validité des objets stockés dans son membre de données. La surcharge AssertValid de CMyData invoque la macro ASSERT_VALID pour sa propre variable membre m_pDataList.
Les tests de validité ne s’arrêtent pas à ce niveau, car la classe CObList remplace AssertValidégalement . Cette surcharge effectue des tests de validité supplémentaires sur l’état interne de la liste. Ainsi, un test de validité sur un CMyData objet entraîne des tests de validité supplémentaires pour les états internes de l’objet de liste stockée CObList .
Avec plus de travail, vous pouvez également ajouter des tests de validité pour les CPerson objets stockés dans la liste. Vous pourriez dériver une classe CPersonList de CObList et remplacer AssertValid. Dans la substitution, vous appelez CObject::AssertValid, puis parcourez la liste, en appelant AssertValid sur chaque élément CPerson de la liste. La CPerson classe affichée au début de cette rubrique remplace AssertValiddéjà .
Il s’agit d’un mécanisme puissant lorsque vous compilez pour le débogage. Lorsque vous générez par la suite pour la mise en production, le mécanisme est désactivé automatiquement.
Limitations de AssertValid
Une assertion déclenchée indique que l’objet est définitivement incorrect et que l’exécution s’arrête. Toutefois, une absence d'assertion indique uniquement qu'aucun problème n’a été trouvé, mais l’objet n’est pas garanti d’être correct.
Utilisation d’assertions
Intercepter des erreurs logiques
Vous pouvez définir une assertion sur une condition qui doit être vraie en fonction de la logique de votre programme. L’assertion n’a aucun effet, sauf si une erreur logique se produit.
Par exemple, supposons que vous simuliez des molécules de gaz dans un conteneur, et que la variable numMols représente le nombre total de molécules. Ce nombre ne peut pas être inférieur à zéro. Vous pouvez donc inclure une instruction d’assertion MFC comme suit :
ASSERT(numMols >= 0);
Vous pouvez également inclure une assertion CRT comme suit :
_ASSERT(numMols >= 0);
Ces instructions ne font rien si votre programme fonctionne correctement. Si une erreur logique entraîne que numMols devient inférieur à zéro, l’assertion arrête toutefois l’exécution de votre programme et affiche la boîte de dialogue Échec de l’assertion.
Vérification des résultats
Les assertions sont précieuses pour les opérations de test dont les résultats ne sont pas évidents à partir d’une inspection visuelle rapide.
Par exemple, considérez le code suivant, qui met à jour la variable iMols en fonction du contenu de la liste liée pointée par 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
Le nombre de molécules comptées par iMols doit toujours être inférieur ou égal au nombre total de molécules. numMols L’inspection visuelle de la boucle ne montre pas que cela sera nécessairement le cas. Par conséquent, une instruction d’assertion est utilisée après la boucle pour tester cette condition.
Recherche d’erreurs non gérées
Vous pouvez utiliser des assertions pour tester les conditions d’erreur à un point dans votre code où toutes les erreurs doivent avoir été gérées. Dans l’exemple suivant, une routine graphique retourne un code d’erreur ou zéro pour la réussite.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Si le code de gestion des erreurs fonctionne correctement, l’erreur doit être gérée et myErr réinitialisée à zéro avant l’atteinte de l’assertion. Si myErr elle a une autre valeur, l’assertion échoue, le programme s’arrête et la boîte de dialogue Échec de l’assertion s’affiche.
Toutefois, les instructions d’assertion ne sont pas un substitut au code de gestion des erreurs. L’exemple suivant montre une instruction d’assertion qui peut entraîner des problèmes dans le code de mise en production final :
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Ce code s’appuie sur l’instruction d’assertion pour gérer la condition d’erreur. Par conséquent, tout code d’erreur retourné par myGraphRoutine sera non géré dans le code de publication final.