Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Uma instrução de asserção especifica uma condição que você espera que seja verdadeira em um ponto do programa. Se essa condição não for verdadeira, a asserção falhará, a execução do programa será interrompida e a caixa de diálogo Falha de Declaração será exibida.
O Visual Studio dá suporte a instruções de asserção C++ baseadas nos seguintes constructos:
Assertivas MFC para programas MFC.
ATLASSERT para programas que usam a ATL.
Asserções CRT para programas que usam a biblioteca de execução C.
A função de declaração ANSI para outros programas C/C++.
Você pode usar asserções para detectar erros lógicos, verificar os resultados de uma operação e testar condições de erro que deveriam ter sido tratadas.
Neste tópico
Declarações em builds de depuração e versão
Efeitos colaterais do uso de declarações
Como funcionam as declarações
Quando o depurador é interrompido devido a uma asserção da biblioteca de tempo de execução MFC ou C, se o código-fonte estiver disponível, o depurador navegará até o ponto no arquivo de origem em que a asserção ocorreu. A mensagem de asserção aparece na janela Saída e na caixa de diálogo Falha de Declaração . Você pode copiar a mensagem de declaração da janela Saída para uma janela de texto se quiser salvá-la para referência futura. A janela Saída também pode conter outras mensagens de erro. Examine essas mensagens com cuidado, pois elas fornecem pistas sobre a causa da falha de declaração.
Use asserções para detectar erros durante o desenvolvimento. Como regra, use uma asserção para cada suposição. Por exemplo, se você assumir que um argumento não é NULL, use uma asserção para testar essa suposição.
Asserções em builds Debug e Release
As instruções de asserção são compiladas somente se _DEBUG estiver definido. Caso contrário, o compilador tratará as declarações como instruções nulas. Portanto, as declarações de asserção não impõem nenhuma sobrecarga ou custo de desempenho no seu programa final de Release e permitem que você evite o uso de diretivas #ifdef.
Efeitos colaterais do uso de declarações
Ao adicionar asserções ao código, verifique se as declarações não têm efeitos colaterais. Por exemplo, considere a seguinte asserção que modifica o nM valor:
ASSERT(nM++ > 0); // Don't do this!
Como a ASSERT expressão não é avaliada na versão Release do programa, nM terá valores diferentes nas versões Debug e Release. Para evitar esse problema no MFC, você pode usar a macro VERIFY em vez de ASSERT.
VERIFY avalia a expressão em todas as versões, mas não verifica o resultado na versão Release.
Tenha cuidado especialmente com o uso de chamadas de função em instruções de asserção, pois avaliar uma função pode ter efeitos colaterais inesperados.
ASSERT ( myFnctn(0)==1 ) // unsafe if myFnctn has side effects
VERIFY ( myFnctn(0)==1 ) // safe
VERIFY chama myFnctn nas versões de Depuração e Release, portanto, é aceitável usá-lo. No entanto, o uso de VERIFY impõe a sobrecarga de uma chamada de função desnecessária na versão de Release.
Declarações de CRT
O arquivo de cabeçalho CRTDBG.H define as macros _ASSERT e _ASSERTE para a verificação de asserções.
| Macro | Resultado |
|---|---|
_ASSERT |
Se a expressão especificada for avaliada como FALSE, o nome do arquivo e o número de linha do _ASSERT. |
_ASSERTE |
O mesmo que _ASSERT, mais uma representação de cadeia de caracteres da expressão que foi declarada. |
_ASSERTE é mais poderoso porque relata a expressão declarada que acabou sendo FALSE. Isso pode ser suficiente para identificar o problema sem se referir ao código-fonte. No entanto, a versão de depuração do aplicativo conterá uma constante de cadeia de caracteres para cada expressão declarada usando _ASSERTE. Se você usar muitas _ASSERTE macros, essas expressões de cadeia de caracteres ocuparão uma quantidade significativa de memória. Se isso for um problema, use _ASSERT para salvar memória.
Quando _DEBUG é definida, a _ASSERTE macro é definida da seguinte maneira:
#define _ASSERTE(expr) \
do { \
if (!(expr) && (1 == _CrtDbgReport( \
_CRT_ASSERT, __FILE__, __LINE__, #expr))) \
_CrtDbgBreak(); \
} while (0)
Se a expressão declarada for avaliada como FALSE, _CrtDbgReport será chamado para relatar a falha de asserção (usando uma caixa de diálogo de mensagem por padrão). Se você escolher Repetir na caixa de diálogo da mensagem, _CrtDbgReport retornará 1 e _CrtDbgBreak chamará o depurador por meio de DebugBreak.
Se você precisar desabilitar temporariamente todas as assertivas, use _CtrSetReportMode.
Verificando se há corrupção de heap
O exemplo abaixo usa _CrtCheckMemory para verificar se há corrupção do heap:
_ASSERTE(_CrtCheckMemory());
Verificando a validade do ponteiro
O exemplo a seguir usa _CrtIsValidPointer para verificar se um determinado intervalo de memória é válido para leitura ou gravação.
_ASSERTE(_CrtIsValidPointer( address, size, TRUE );
O exemplo a seguir usa _CrtIsValidHeapPointer para verificar se um ponteiro aponta para a memória no heap local (o heap criado e gerenciado por essa instância da biblioteca de tempo de execução C – uma DLL pode ter sua própria instância da biblioteca e, portanto, seu próprio heap, fora do heap de aplicativos). Essa asserção captura não apenas endereços nulos ou fora dos limites, mas também ponteiros para variáveis estáticas, variáveis de pilha e qualquer outra memória não local.
_ASSERTE(_CrtIsValidHeapPointer( myData );
Verificando um bloco de memória
O exemplo a seguir usa _CrtIsMemoryBlock para verificar se um bloco de memória está no heap local e tem um tipo de bloco válido.
_ASSERTE(_CrtIsMemoryBlock (myData, size, &requestNumber, &filename, &linenumber));
Declarações MFC
O MFC define a macro ASSERT para verificação de asserção. Ele também define os métodos MFC ASSERT_VALID e CObject::AssertValid para verificar o estado interno de um CObject-objeto derivado.
Se o argumento da macro MFC ASSERT for avaliado como zero ou falso, a macro interromperá a execução do programa e alertará o usuário; caso contrário, a execução continuará.
Quando uma asserção falha, uma caixa de diálogo de mensagem mostra o nome do arquivo de origem e o número de linha da declaração. Se você escolher Repetir na caixa de diálogo, uma chamada para AfxDebugBreak fará com que a execução seja interrompida para o depurador. Nesse ponto, você pode examinar a pilha de chamadas e usar outras ferramentas do depurador para determinar por que a asserção falhou. Se você tiver habilitado a depuração just-in-time e o depurador ainda não estiver em execução, a caixa de diálogo poderá iniciar o depurador.
O exemplo a seguir mostra como usar ASSERT para verificar o valor retornado de uma função:
int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
Você pode usar ASSERT com a função IsKindOf para fornecer verificação de tipo de argumentos de função:
ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
A ASSERT macro não produz nenhum código na versão de lançamento. Se você precisar avaliar a expressão na versão de lançamento, use a macro VERIFY em vez de ASSERT.
MFC ASSERT_VALID e CObject::AssertValid
O método CObject::AssertValid fornece verificações em tempo de execução do estado interno de um objeto. Ainda que você não seja obrigado a substituir AssertValid quando derivar sua classe de CObject, você pode tornar sua classe mais confiável fazendo isso.
AssertValid deve executar asserções em todas as variáveis de membro do objeto para verificar se elas contêm valores válidos. Por exemplo, ele deve verificar se as variáveis de membro ponteiro não são null.
O exemplo a seguir mostra como declarar uma AssertValid função:
class CPerson : public CObject
{
protected:
CString m_strName;
float m_salary;
public:
#ifdef _DEBUG
// Override
virtual void AssertValid() const;
#endif
// ...
};
Ao substituir AssertValid, chame a versão da classe base de AssertValid antes de executar suas próprias verificações. Em seguida, use a macro ASSERT para verificar os membros exclusivos para sua classe derivada, conforme mostrado aqui:
#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
Se qualquer uma das variáveis membros armazenar objetos, você poderá usar o ASSERT_VALID macro para testar sua validade interna (se suas classes sobrescreverem AssertValid).
Por exemplo, considere uma classe CMyData, que armazena um CObList em uma de suas variáveis de membro. A CObList variável armazena m_DataListuma coleção de CPerson objetos. Uma declaração abreviada de CMyData se parece com isto:
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 substituição AssertValid em CMyData se parece com isto:
#ifdef _DEBUG
void CMyData::AssertValid( ) const
{
// Call inherited AssertValid.
CObject::AssertValid( );
// Check validity of CMyData members.
ASSERT_VALID( m_pDataList );
// ...
}
#endif
CMyData usa o AssertValid mecanismo para testar a validade dos objetos armazenados em seu membro de dados. A substituição AssertValid de CMyData invoca a macro ASSERT_VALID para sua própria variável membro m_pDataList.
O teste de validade não é interrompido nesse nível porque a classe CObList também substitui AssertValid. Essa sobrescrição executa testes de validade adicionais no estado interno da lista. Assim, um teste de validade em um CMyData objeto leva a testes de validade adicionais para os estados internos do objeto de lista armazenado CObList .
Com um pouco mais de trabalho, você também pode adicionar testes de validade para os CPerson objetos armazenados na lista. Você pode derivar uma classe CPersonList de CObList e sobrescrever AssertValid. Na sobrescrição, você chamaria CObject::AssertValid e iteraria pela lista, chamando AssertValid em cada objeto CPerson armazenado na lista. A CPerson classe mostrada no início deste tópico já substitui AssertValid.
Esse é um mecanismo poderoso quando você constrói para depuração. Quando você posteriormente prepara a versão de lançamento, o mecanismo é desativado automaticamente.
Limitações de AssertValid
Uma asserção disparada indica que o objeto é definitivamente inválido e a execução será interrompida. No entanto, a falta de declaração indica apenas que nenhum problema foi encontrado, mas o objeto não tem garantia de ser bom.
Usando asserções
Capturando erros lógicos
Você pode definir uma asserção em uma condição que deve ser verdadeira de acordo com a lógica do seu programa. A asserção não tem efeito, a menos que ocorra um erro lógico.
Por exemplo, suponha que você esteja simulando moléculas de gás em um contêiner e que a variável numMols represente o número total de moléculas. Esse número não pode ser menor que zero, portanto, você pode incluir uma declaração MFC como o seguinte:
ASSERT(numMols >= 0);
Ou você pode incluir uma declaração CRT como esta:
_ASSERT(numMols >= 0);
Essas instruções não farão nada se o programa estiver operando corretamente. No entanto, se um erro lógico fizer com que numMols seja menor que zero, a asserção interromperá a execução do seu programa e exibirá a Caixa de Diálogo de Falha de Asserção.
Verificando resultados
As declarações são valiosas para operações de teste cujos resultados não são óbvios de uma inspeção visual rápida.
Por exemplo, considere o código a seguir, que atualiza a variável iMols com base no conteúdo da lista vinculada apontada por 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
O número de moléculas contadas por iMols deve ser sempre menor ou igual ao número total de moléculas, numMols. A inspeção visual do loop não mostra que isso ocorrerá necessariamente, portanto, uma asserção é usada após o loop para testar essa condição.
Localizando erros sem tratamento
Você pode usar asserções para testar condições de erro em um ponto em seu código em que quaisquer erros deveriam ter sido tratados. No exemplo a seguir, uma rotina gráfica retorna um código de erro ou zero para êxito.
myErr = myGraphRoutine(a, b);
/* Code to handle errors and
reset myErr if successful */
ASSERT(!myErr); -- MFC version
_ASSERT(!myErr); -- CRT version
Se o código de tratamento de erros funcionar corretamente, o erro deverá ser tratado e myErr redefinido para zero antes que a assertiva seja atingida. Se myErr tiver outro valor, a asserção falhará, o programa será interrompido e a Caixa de Diálogo Falha de Declaração será exibida.
No entanto, as instruções de declaração não substituem o código de tratamento de erros. O exemplo a seguir mostra uma declaração de asserção que pode levar a problemas no código final:
myErr = myGraphRoutine(a, b);
/* No Code to handle errors */
ASSERT(!myErr); // Don't do this!
_ASSERT(!myErr); // Don't do this, either!
Esse código depende da instrução de asserção para lidar com a condição de erro. Como resultado, qualquer código de erro retornado por myGraphRoutine será sem tratamento no código de versão final.