Aserciones
Una instrucción de aserción especifica una condición que se espera que sea true en un punto del programa.Si la condición no es true, la aserción, la ejecución del programa se interrumpe, y El cuadro de diálogo error de aserción aparece.
Visual C++ admite las instrucciones de aserción basados en las construcciones siguientes:
Aserciones de MFC para programas MFC.
ATLASSERT para programas que utilizan ATL.
Aserciones de CRT para programas que utilizan la biblioteca en tiempo de ejecución de C.
La función assert de ANSI para otros programas en C o C++.
Puede utilizar aserciones para detectar los errores lógicos, comprobar resultados de una operación, y las condiciones de error de la prueba que deberían haberse controlado.
En este tema
Cómo funcionan las aserciones
Aserciones en las compilaciones de depuración y de lanzamiento
Efectos secundarios de utilizar aserciones
Aserciones de CRT
Aserciones de MFC
MFC ASSERT_VALID y CObject::AssertValid
Limitaciones de AssertValid
Mediante aserciones
Errores lógicos de detección
Comprobar los resultados
Buscar errores no controlados
Cómo funcionan las aserciones
Cuando el depurador se detiene debido a MFC o aserción de la biblioteca en tiempo de ejecución de C, después si el origen está disponible, el depurador navega hasta el punto en el archivo de código fuente donde la aserción.El mensaje de aserción aparece en Ventana de salida y el cuadro de diálogo Error de aserción .El mensaje de aserción se puede copiar desde la Ventana de salida en una ventana de texto si se desea guardarlo para consultas posteriores.La Ventana de salida puede contener también otros mensajes de error.Examine estos mensajes con cuidado, ya que pueden proporcionar pistas para encontrar la causa del error de aserción.
Aserciones de uso para detectar errores durante el desarrollo.En general, utilice una aserción para cada suposición.Por ejemplo, si supone que un argumento no es NULL, utilice una aserción para probar esa suposición.
En este tema
Aserciones en las compilaciones de depuración y de lanzamiento
Las instrucciones de aserción compilan sólo si se define _DEBUG .Si no, el compilador trata las aserciones como instrucciones null.Por consiguiente, las instrucciones de aserción no impone ninguna sobrecarga o el rendimiento costó en el programa de inicio final, y permite evitar utilizar las directivas de #ifdef .
Efectos secundarios de utilizar aserciones
Cuando agregue aserciones al código, asegúrese de que no producen efectos secundarios.Por ejemplo, considere la siguiente aserción que modifique el valor de nM :
ASSERT(nM++ > 0); // Don't do this!
Dado que la expresión de ASSERT no se evalúa en la versión de lanzamiento del programa, nM tendrá valores diferentes en las versiones de depuración y de lanzamiento.Para evitar este problema en MFC, puede utilizar la macro de COMPRUEBE en lugar de ASSERT.VERIFY evalúa la expresión en todas las versiones pero no comprueba el resultado en la versión de lanzamiento.
Tenga especial cuidado al utilizar llamadas a funciones en las instrucciones de aserción, ya que la evaluación de una función puede producir efectos laterales inesperados.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY llama myFnctn en las versiones de depuración y lanzamiento, de modo que puede utilizar.Sin embargo, mediante VERIFY impone la sobrecarga de una llamada de función innecesaria en la versión de lanzamiento.
En este tema
Aserciones de CRT
El archivo de encabezado CRTDBG.H define las macros _ASSERT y _ASSERTE para la comprobación de aserciones.
Macro |
Resultado |
---|---|
_ASSERT |
Si la expresión especificada se evalúa como FALSE, el resultado es el nombre de archivo y el número de línea de la expresión sometida a _ASSERT. |
_ASSERTE |
Igual que _ASSERT, más una representación de cadena de la expresión sometida a aserción. |
_ASSERTE es más eficaz, ya que informa de la expresión sometida a aserción que resultó ser falsa (valor FALSE).Esto puede ser suficiente para identificar el problema sin consultar el código fuente.No obstante, la versión de depuración de la aplicación contiene una constante de tipo cadena para cada expresión sometida a aserción mediante _ASSERTE.Si se utilizan muchas macros _ASSERTE, estas expresiones de cadena ocupan una cantidad de memoria significativa.Si eso constituye un problema, utilice _ASSERT para ahorrar memoria.
Cuando se define _DEBUG , la macro se define de _ASSERTE como sigue:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Si la expresión sometida a aserción se evalúa como FALSE, se realiza una llamada a _CrtDbgReport para informar del error de aserción (mediante un cuadro de diálogo de mensaje, de forma predeterminada).Si elige Reintentar en el cuadro de mensaje, _CrtDbgReport devuelve 1 y _CrtDbgBreak llama el depurador con DebugBreak.
Comprobar si el montón está dañado
El siguiente ejemplo usa _CrtCheckMemory para comprobar si el montón ha sufrido algún daño:
_ASSERTE(_CrtCheckMemory());
Comprobar la validez de los punteros
El siguiente ejemplo usa _CrtIsValidPointer para comprobar si un determinado intervalo de memoria es válido para lectura o escritura.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
El siguiente ejemplo utiliza _CrtIsValidHeapPointer para comprobar si un puntero apunta a la memoria del montón local, que es el montón creado y administrado por esta instancia de la biblioteca en tiempo de ejecución de C (una DLL puede tener su propia instancia de la biblioteca y, por tanto, su propio montón, independiente del montón de la aplicación).Esta aserción captura no sólo direcciones null o fuera de límites, sino también punteros a variables estáticas, variables de pila y cualquier otra memoria no local.
_ASSERTE(_CrtIsValidPointer( myData );
Comprobar un bloque de memoria
El siguiente ejemplo utiliza _CrtIsMemoryBlock para comprobar si un bloque de memoria se encuentra en el montón local y tiene un tipo de bloque válido.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
En este tema
Aserciones de MFC
MFC define la macro ASSERT para comprobar aserciones.También define los métodos de MFC ASSERT_VALID y de CObject::AssertValid para comprobar el estado interno de CObject- objeto derivado.
Si el argumento de la macro de MFC ASSERT se evalúa como cero o en false, la macro detiene la ejecución de un programa y alerta al usuario; si no, la ejecución continúa.
Cuando se produce un error de aserción, un cuadro de mensaje muestra el nombre del archivo de código fuente y el número de línea de la aserción.Si se elige Reintentar en el cuadro de diálogo, una llamada a AfxDebugBreak hace que la ejecución se interrumpa y se inicie el depurador.En ese momento, puede examinar la pila de llamadas y utilizar otros servicios del depurador para determinar el error de aserción.Si ha habilitado Depuración Just-In-Time, y el depurador no estaba ejecutando ya, el cuadro de diálogo puede iniciar el depurador.
El ejemplo siguiente se muestra cómo utilizar ASSERT para comprobar el valor devuelto de una función:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Se puede utilizar ASSERT con la función IsKindOf para proporcionar la comprobación de tipos de los argumentos de una función:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
La macro de ASSERT no muestra ningún código de la versión de lanzamiento.Si necesita evaluar la expresión en la versión de lanzamiento, utilice la macro VERIFY en vez de ASSERT.
MFC ASSERT_VALID y CObject::AssertValid
El método CObject::AssertValid proporciona comprobaciones en tiempo de ejecución del estado interno de un objeto .Aunque no es obligatorio reemplazar AssertValid al derivar la clase de CObject, puede conseguir una clase más confiable si lo hace.AssertValid debe realizar aserciones en las variables miembro del objeto para comprobar que contienen valores válidos.Por ejemplo, debería comprobar que las variables miembro no sean NULL.
En el ejemplo siguiente, se muestra cómo declarar una función AssertValid:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
Cuando invalide AssertValid, llame a la versión de la clase base de AssertValid antes de ejecutar sus propias comprobaciones.Después, use la macro ASSERT para comprobar los miembros únicos de la clase derivada, como se muestra a continuación:
#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 cualquiera de las variables miembro almacenan objetos, se puede utilizar la macro ASSERT_VALID para probar su validez interna (si sus clases reemplazan la función AssertValid).
Por ejemplo, considere una clase CMyData, la cual almacena un objeto CObList en una de sus variables miembro.La variable CObList, m_DataList, almacena una colección de objetos CPerson.Una declaración abreviada de CMyData tiene el siguiente aspecto:
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 ...
};
El sustituto de AssertValid en CMyData es:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData utiliza el mecanismo AssertValid para probar la validez de los objetos almacenados en su miembro de datos.El sustituto AssertValid de CMyData invoca la macro ASSERT_VALID para su propia variable miembro m_pDataList.
Pruebas de validez no detiene en este nivel porque la clase CObList también invalida AssertValid.Esta sustitución ejecuta pruebas de validez adicionales sobre el estado interno de la lista.Así, una prueba de validez sobre un objeto CMyData conduce a pruebas de validez adicionales sobre los estados internos del objeto de lista CObList almacenado.
Con algo más de trabajo, se podrían también agregar pruebas de validez para los objetos CPerson almacenados en la lista.Se podría derivar una clase CPersonList de CObList y reemplazar AssertValid.En el método reemplazado, se llamaría a CObject::AssertValid y, a continuación, la lista se recorrería en iteración, llamando a AssertValid para cada objeto CPerson almacenado en la lista.La clase CPerson mostrada al principio de este tema ya reemplaza la función AssertValid.
Se trata de un mecanismo muy eficaz de las versiones de depuración.Cuando, posteriormente, se compilan versiones de lanzamiento, el mecanismo se desactiva automáticamente.
Limitaciones de AssertValid
Si se desencadena una aserción, el objeto es definitivamente defectuoso y la ejecución se detendrá.Sin embargo, la falta de aserción sólo indica que no se encontró ningún problema, pero no garantiza que el objeto sea correcto.
En este tema
Mediante aserciones
Errores lógicos de detección
Se puede definir una aserción sobre una condición que debe ser cierta según la lógica del programa.La aserción no tiene ningún efecto a menos que se produzca un error de lógica.
Por ejemplo, suponga que está simulando moléculas de gas en un contenedor y que la variable numMols representa el número total de moléculas.Este número no puede ser menor que cero, por tanto, se podría incluir una instrucción de aserción de MFC como ésta:
ASSERT(numMols >= 0);
O bien, podría incluir una aserción de CRT como ésta:
_ASSERT(numMols >= 0);
Estas instrucciones no hacen nada si el programa funciona correctamente.Si un error lógico hace numMolssea menor que cero, sin embargo, la aserción detiene la ejecución del programa y muestra Cuadro de diálogo Error de aserción.
En este tema
Comprobar los resultados
Las aserciones tienen valores para operaciones cuyos resultados no son obvios con una inspección visual.
Por ejemplo, considere el siguiente código, que actualiza la variable iMols según el contenido de la lista vinculada a la que apunta 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
El número de moléculas contadas por iMols debe ser siempre menor o igual que el número total de moléculas, numMols.La inspección visual del bucle no muestra que éste sea necesariamente el caso, por lo que se utiliza una instrucción de aserción después del bucle para probar esa condición.
En este tema
Buscar errores no controlados
Se pueden utilizar aserciones para probar condiciones de error en un punto del código en el que cualquier error se debería haber controlado.En el siguiente ejemplo, una rutina gráfica devuelve cero si no hay error, o un código de error, en caso contrario.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Si el código de tratamiento del error funciona correctamente, el error debería recibir el tratamiento adecuado y myErr se debería restablecer a cero antes de alcanzar la aserción.Si myErr tiene otro valor, se produce un error en la aserción, el programa se detiene y aparece Cuadro de diálogo Error de aserción.
No obstante, las instrucciones de aserción no son un sustituto del código de control de errores.El siguiente ejemplo muestra una instrucción de aserción que puede causar problemas en el código final de la versión de lanzamiento:
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Este código se basa en la instrucción de aserción para controlar la condición de error.Como resultado, ningún código de error devuelto por myGraphRoutine dispondrá de tratamiento en el código de la versión de lanzamiento.
En este tema
Vea también
Referencia
Aserciones en el código administrado