Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. 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 seu 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 na asserção será exibida.
Visual Studio oferece suporte a instruções de asserção C++ que são baseadas nas seguintes construções:
Asserções MFC para programas MFC.
ATLASSERT é utilizado em programas que usam ATL.
Asserções CRT para programas que usam a biblioteca de tempo de execução C.
A função assert da ANSI para outros programas em C/C++.
Você pode usar asserções para detetar erros de lógica, verificar resultados de uma operação e testar condições de erro que deveriam ter sido tratadas.
Neste tópico
Asserções em compilações de depuração e versão
Efeitos colaterais do uso de asserções
Como funcionam as asserções
Quando o depurador para devido a uma asserção de uma biblioteca de tempo de execução MFC ou C, se o código fonte estiver disponível, o depurador navega para o ponto no arquivo de origem onde a asserção ocorreu. A mensagem de asserção aparece na janela Saída e na caixa de diálogo Falha na asserção . Você pode copiar a mensagem de asserçã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 cuidadosamente, pois elas fornecem pistas sobre a causa da falha de asserção.
Use asserções para detetar 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 compilações de depuração e versão
As declarações de asserção são compiladas somente se _DEBUG forem definidas. Caso contrário, o compilador trata asserções como instruções nulas. Portanto, as declarações de asserção não impõem nenhuma sobrecarga ou custo de desempenho em seu programa de versão final e permitem que você evite o uso de #ifdef diretivas.
Efeitos colaterais do uso de asserções
Quando você adiciona asserções ao seu código, certifique-se de que as asserções não tenham 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 seu 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 de lançamento.
Tenha cuidado especial ao usar chamadas de função em declarações de asserção, porque 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 em ambas as versões Debug e Release, por isso é aceitável usar. No entanto, o uso VERIFY impõe a sobrecarga de uma chamada de função desnecessária na versão de lançamento.
Afirmações CRT
O CRTDBG.H arquivo de cabeçalho define as _ASSERT macros e _ASSERTE para verificação de asserção.
| Macro | Result |
|---|---|
_ASSERT |
Se a expressão especificada for avaliada como FALSE, o nome do arquivo e o número da linha associados serão apresentados, _ASSERT. |
_ASSERTE |
O mesmo que _ASSERT, além de uma representação em cadeia da expressão afirmada. |
_ASSERTE é mais poderoso porque relata a expressão afirmada que se revelou FALSA. Isso pode ser suficiente para identificar o problema sem se referir ao código-fonte. No entanto, a versão de depuração do seu 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 ocupam uma quantidade significativa de memória. Se isso provar ser um problema, use _ASSERT para economizar memória.
Quando _DEBUG é definida, a _ASSERTE macro é definida da seguinte forma:
#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 escolher Repetir na caixa de diálogo da mensagem, _CrtDbgReport retornará 1 e _CrtDbgBreak chamará o depurador através de DebugBreak.
Se precisa desativar temporariamente todas as afirmações, use _CtrSetReportMode.
Verificando se há corrupção de heap
O exemplo a seguir 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 esta 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 do aplicativo). 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 );
Verificar 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));
Afirmações MFC
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 objeto derivado de CObject.
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 da linha da asserçã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 outros recursos do depurador para determinar por que a asserção falhou. Se você tiver ativado 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 de retorno 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 Release, 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. Embora você não seja obrigado a substituir AssertValid quando deriva sua classe do CObject, você pode tornar sua classe mais confiável fazendo isso.
AssertValid deve executar asserções em todas as variáveis membro do objeto para verificar se elas contêm valores válidos. Por exemplo, ele deve verificar se as variáveis de membro do 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
// ...
};
Quando substituir o método AssertValid, chame a versão da classe base de AssertValid antes de executar as 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 membro armazenar objetos, você poderá usar a ASSERT_VALID macro para testar sua validade interna (se suas classes substituírem AssertValid).
Por exemplo, considere uma classe CMyData, que armazena um CObList em uma de suas variáveis membro. A CObList variável, m_DataList, armazena uma coleção de CPerson objetos. Uma declaração abreviada de CMyData tem esta aparência:
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 em CMyData tem esta aparência:
#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 de CMyData de AssertValid invoca a macro ASSERT_VALID para a sua própria variável membro m_pDataList.
Os testes de validade não param neste nível porque a classe CObList também substitui AssertValid. Esta substituiçã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 armazenada CObList .
Com mais algum trabalho, você pode adicionar testes de validade para os CPerson objetos armazenados na lista também. Você pode derivar uma classe CPersonList de CObList e substituir AssertValid. Na função de substituição, você chamaria CObject::AssertValid e, em seguida, percorreria a lista, chamando AssertValid em cada objeto CPerson armazenado na lista. A CPerson classe mostrada no início deste tópico já substitui AssertValid.
Este é um mecanismo poderoso quando você cria para depuração. Quando você compila posteriormente para liberação, o mecanismo é desativado automaticamente.
Limitações de AssertValid
Uma asserção acionada indica que o objeto é definitivamente ruim e a execução será interrompida. No entanto, a falta de asserção indica apenas que nenhum problema foi encontrado, mas o objeto não tem garantia de ser bom.
Usando asserções
Detetar erros de lógica
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 de lógica.
Por exemplo, suponha que você esteja simulando moléculas de gás em um recipiente e 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 instrução de asserção MFC como esta:
ASSERT(numMols >= 0);
Ou você pode incluir uma afirmação CRT como esta:
_ASSERT(numMols >= 0);
Estas instruções não fazem nada se o seu programa estiver funcionando corretamente. No entanto, se um erro de lógica fizer com que numMols seja menor que zero, a declaração interromperá a execução do programa e exibirá a caixa de diálogo de falha de declaração.
Verificação dos resultados
As asserções são valiosas para testar operações cujos resultados não são óbvios a partir de uma rápida inspeção visual.
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 inferior ou igual ao número total de moléculas, numMols. A inspeção visual do loop não mostra que este será necessariamente o caso, de modo que uma declaração de asserção é usada após o loop para testar essa condição.
Localizando erros não tratados
Você pode usar asserções para testar condições de erro em um ponto do 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 deve ser tratado e myErr redefinido para zero antes que a asserção seja atingida. Se myErr tiver outro valor, a asserção falhará, o programa será interrompido e a caixa de diálogo Assertion Failed (Falha na asserção ) será exibida.
No entanto, as instruções de asserção não substituem o código de tratamento de erros. O exemplo a seguir mostra uma instrução de asserção que pode levar a problemas no código de versão 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 depende da declaração de asserção para manipular a condição de erro. Como resultado, qualquer código de erro retornado por myGraphRoutine não será tratado no código de versão final.