Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Una instrucción de aserción especifica una condición que esperas que sea verdadera en un determinado punto de tu programa. Si esa condición no es cierta, se produce un error en la aserción, se interrumpe la ejecución del programa y aparece el cuadro de diálogo Error de aserción .
Visual Studio admite instrucciones de aserción de C++ basadas en las siguientes construcciones:
Aserciones de MFC para programas MFC.
ATLASSERT para programas que usan ATL.
Aserciones de CRT para programas que usan la biblioteca de tiempo de ejecución de C.
Función assert ANSI para otros programas de C/C++.
Puede usar aserciones para detectar errores lógicos, comprobar los resultados de una operación y Probar condiciones de error que se deben controlar.
En este tema
Funcionamiento de las aserciones
Aserciones en compilaciones de depuración y liberación
Efectos secundarios del uso de aserciones
Funcionamiento de las aserciones
Cuando el depurador se detiene debido a una aserción de biblioteca en tiempo de ejecución de MFC o C, si el origen está disponible, el depurador navega al punto del archivo de origen donde se produjo la aserción. El mensaje de aserción aparece en la ventana Salida y en el cuadro de diálogo Error de aserción . Puede copiar el mensaje de aserción de la ventana Salida en una ventana de texto si desea guardarlo para una referencia futura. La ventana Salida también puede contener otros mensajes de error. Examine detenidamente estos mensajes, ya que proporcionan pistas para la causa del error de aserción.
Use aserciones para detectar errores durante el desarrollo. Como regla, use una aserción para cada suposición. Por ejemplo, si supone que un argumento no es NULL, use una aserción para probar esa suposición.
Aserciones en compilaciones de depuración y versión
Las instrucciones de aserción solo se compilan si se ha definido _DEBUG. De lo contrario, el compilador trata las aserciones como instrucciones nulas. Por lo tanto, las instrucciones de aserción no imponen ninguna sobrecarga ni costo de rendimiento en su programa final de versión Release y permiten evitar el uso de #ifdef directivas.
Efectos secundarios del uso de aserciones
Al agregar aserciones al código, asegúrese de que las aserciones no tienen efectos secundarios. Por ejemplo, considere la siguiente aserción que modifica el nM valor:
ASSERT(nM++ > 0); // Don't do this!
Dado que la ASSERT expresión no se evalúa en la versión release del programa, nM tendrá valores diferentes en las versiones Debug y Release. Para evitar este problema en MFC, puede usar la macro VERIFY 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 con el uso de llamadas de función en sentencias de aserción, ya que evaluar una función puede tener efectos inesperados.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY llama a myFnctn en las versiones Debug y Release, por lo que es aceptable usar. Sin embargo, el uso de VERIFY impone la sobrecarga de una llamada de función innecesaria en la versión de producción.
Aserciones de CRT
El CRTDBG.H archivo de encabezado define las _ASSERT macros y _ASSERTE para la comprobación de aserciones.
| Macro | Resultado |
|---|---|
_ASSERT |
Si la expresión especificada se evalúa como FALSE, el nombre de archivo y el número de línea de _ASSERT. |
_ASSERTE |
Igual que _ASSERT, además de una representación de cadena de la expresión que se afirmó. |
_ASSERTE es más potente porque informa sobre la expresión afirmada que resultó ser FALSO. Esto puede ser suficiente para identificar el problema sin hacer referencia al código fuente. Sin embargo, la versión de depuración de la aplicación contendrá una constante de cadena para cada expresión aserida mediante _ASSERTE. Si usa muchas _ASSERTE macros, estas expresiones de cadena ocupan una cantidad significativa de memoria. Si eso demuestra ser un problema, use _ASSERT para guardar memoria.
Cuando _DEBUG se define, la _ASSERTE macro se define de la siguiente manera:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Si la expresión asertada se evalúa como FALSE, se invoca _CrtDbgReport para notificar el fallo de aserción (mediante un cuadro de diálogo de mensaje de forma predeterminada). Si elige Reintentar en el cuadro de diálogo de mensaje, _CrtDbgReport devuelve 1 y _CrtDbgBreak llama al depurador a través de DebugBreak.
Si necesita deshabilitar temporalmente todas las aserciones, use _CtrSetReportMode.
Comprobación de daños en el montón
En el ejemplo siguiente se usa _CrtCheckMemory para comprobar si hay daños en el montón:
_ASSERTE(_CrtCheckMemory());
Comprobación de la validez del puntero
En el ejemplo siguiente se usa _CrtIsValidPointer para comprobar que un intervalo de memoria determinado es válido para leer o escribir.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
En el ejemplo siguiente se usa _CrtIsValidHeapPointer para comprobar que un puntero apunta a la memoria en el montón local (el montón creado y administrado por esta instancia de la biblioteca en tiempo de ejecución de C: un archivo DLL puede tener su propia instancia de la biblioteca y, por tanto, su propio montón, fuera del montón de la aplicación). Esta aserción detecta no solo direcciones nulas o fuera de los límites, sino también punteros a variables estáticas, variables de pila y cualquier otra memoria que no sea local.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Comprobación de un bloque de memoria
En el ejemplo siguiente se usa _CrtIsMemoryBlock para comprobar que un bloque de memoria está en el montón local y tiene un tipo de bloque válido.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
Aserciones de MFC
MFC define la macro ASSERT para la comprobación de aserciones. También define los métodos MFC ASSERT_VALID y CObject::AssertValid para comprobar el estado interno de un objeto derivado de CObject.
Si el argumento de la macro MFC ASSERT se evalúa como cero o false, la macro detiene la ejecución del programa y alerta al usuario; de lo contrario, la ejecución continúa.
Cuando se produce un error en una aserción, un cuadro de diálogo de mensaje muestra el nombre del archivo de origen y el número de línea de la aserción. Si elige Reintentar en el cuadro de diálogo, una llamada a AfxDebugBreak hace que la ejecución se interrumpa en el depurador. En ese momento, puede examinar la pila de llamadas y utilizar otras herramientas del depurador para determinar por qué la aserción falló. Si ha habilitado la depuración Just-In-Time y el depurador aún no se estaba ejecutando, el cuadro de diálogo puede iniciar el depurador.
En el ejemplo siguiente se muestra cómo usar ASSERT para comprobar el valor devuelto de una función:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Puede usar ASSERT con la función IsKindOf para proporcionar la comprobación de tipos de argumentos de función:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
La ASSERT macro no genera ningún código en la versión de lanzamiento. Si necesita evaluar la expresión en la versión release, use la macro VERIFY en lugar 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 necesario invalidar AssertValid cuando derive la clase de CObject, puede hacer que la clase sea más confiable haciendo esto.
AssertValid debe realizar aserciones en todas las variables miembro del objeto para comprobar que contienen valores válidos. Por ejemplo, debe comprobar que las variables miembro de puntero no estén en NULL.
En el ejemplo siguiente se muestra cómo declarar una AssertValid función:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
Al invalidar AssertValid, llame a la versión de la clase base de AssertValid antes de realizar sus propias comprobaciones. A continuación, use la macro ASSERT para comprobar los miembros únicos de la clase derivada, como se muestra aquí:
#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 alguna de las variables miembro almacena objetos, puede usar la ASSERT_VALID macro para probar su validez interna (si sus clases invalidan AssertValid).
Por ejemplo, considere una clase CMyData, que almacena un CObList en una de sus variables miembro. La CObList variable , m_DataList, almacena una colección de CPerson objetos . Una declaración abreviada de CMyData tiene este 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 ...
};
La AssertValid sobrescritura en CMyData tiene este aspecto:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData usa el AssertValid mecanismo para probar la validez de los objetos almacenados en su miembro de datos. La sobrescritura AssertValid de CMyData invoca la macro ASSERT_VALID para su propio variable miembro m_pDataList.
Las pruebas de validez no se detienen en este nivel porque la clase CObList también invalida AssertValid. Esta sobrescritura ejecuta pruebas adicionales de validez en el estado interno de la lista. Por lo tanto, una prueba de validez en un CMyData objeto conduce a pruebas de validez adicionales para los estados internos del objeto de lista almacenado CObList .
Con algún trabajo más, también podría agregar pruebas de validez para los CPerson objetos almacenados en la lista. Puede derivar una clase CPersonList de CObList e invalidar AssertValid. En la anulación, llamaría a CObject::AssertValid y, a continuación, recorrería la lista, llamando a AssertValid en cada objeto CPerson almacenado en la lista. La CPerson clase que se muestra al principio de este tema ya invalida AssertValid.
Este es un mecanismo potente al construir para depuración. Cuando se compila posteriormente para la versión, el mecanismo se desactiva automáticamente.
Limitaciones de AssertValid
Una verificación activada indica que el objeto es definitivamente inválido y la ejecución del programa se detendrá. Sin embargo, una falta de aserción indica solo que no se encontró ningún problema, pero no se garantiza que el objeto sea bueno.
Uso de aserciones
Detección de errores lógicos
Puede establecer una aserción en una condición que debe ser verdadera según la lógica de su programa. La aserción no tiene ningún efecto a menos que se produzca un error lógico.
Por ejemplo, supongamos que está simulando moléculas de gas en un contenedor, y la variable numMols representa el número total de moléculas. Este número no puede ser menor que cero, por lo que puede incluir una declaración de aserción de MFC como esta:
ASSERT(numMols >= 0);
O bien, puede incluir una aserción de CRT como esta:
_ASSERT(numMols >= 0);
Estas instrucciones no hacen nada si el programa funciona correctamente. Sin embargo, si un error lógico provoca numMols que sea menor que cero, la aserción detiene la ejecución del programa y muestra el cuadro de diálogo Error de aserción.
Comprobación de los resultados
Las aserciones son valiosas para las operaciones de prueba cuyos resultados no son obvios a partir de una inspección visual rápida.
Por ejemplo, considere el siguiente código, que actualiza la variable iMols en función del contenido de la lista enlazada 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 siempre debe ser menor o igual que el número total de moléculas, numMols. La inspección visual del bucle no muestra que esto será necesariamente el caso, por lo que se usa una instrucción de aserción después del bucle para probar esa condición.
Búsqueda de errores no gestionados
Puede usar aserciones para probar las condiciones de error en un punto del código en el que se deben controlar los errores. En el ejemplo siguiente, una rutina gráfica devuelve un código de error o cero para éxito.
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 control de errores funciona correctamente, el error debe controlarse y myErr restablecerse a cero antes de que se alcance la aserción. Si myErr tiene otro valor, se produce un error en la aserción, se detiene el programa y aparece el cuadro de diálogo Error de aserción .
Sin embargo, las instrucciones de aserción no son un sustituto del código de control de errores. En el ejemplo siguiente se muestra una instrucción de aserción que puede provocar problemas en el código de versión final:
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, cualquier código de error devuelto por myGraphRoutine quedará sin gestionar en el código de la versión final.