Глубокие проверки корректности
Та часть расширения MemDumperValidator, которая имеет дело с выдачей дампов, бесспорно полезна, но можно задать вопрос: "Зачем вообще нужен метод проверки корректности, даже если он позволяет осуществлять "глубокую" проверку блока памяти?" Если класс содержит всего пару строчных переменных, то во многих случаях функция проверки корректности может быть "пустой". Но даже в этом случае такая функция может оказаться бесценной для разработчика, потому что она обеспечивает его превосходными отладочными возможностями. Одной из целей, которой я стремился достичь, начиная пользоваться глубокой проверкой корректности памяти, было обеспечение второго уровня проверки корректности данных на наборе разработанных мной базовых классов. Функция проверки корректности не должна заменять обычных проверок параметров и вводимых данных, но может повысить степень уверенности в правильности этих данных. Глубокая проверка корректности может также быть второй линией защиты против "непредсказуемых" (wild) записей.
Лучше всего применять функции проверки корректности для двойной проверки сложных структур данных после того, как на них были выполнены некоторые операции. Например, однажды я попал в довольно сложную ситуацию, когда две отдельные рекурсивные структуры данных (self-referential data structures) использовали (по соображениям экономии памяти) одни и те же распределенные объекты. Заполнив эти структуры большими наборами данных, я с помощью функции проверки корректности просматривал индивидуальные блоки кучи и проверял корректность ссылок. Можно было написать обстоятельную программу для просмотра каждой структуры данных, но я знал, что любая такая программа стала бы новым рассадником ошибок. А функция проверки корректности позволила "проскакивать" через распределенные блоки, используя уже протестированный код, и проверять структуры данных, начиная с различных позиций, потому что память была выстроена в порядке распределения, а не в отсортированном порядке.
Операции распределения памяти языка С более сложны, чем в языке C++, но функции проверки корректности памяти в обоих языках используются одинаково. Все что нужно делать — это вызывать макрос VALIDATEALLBLOCKS. В отладочных построениях этот макрос расширяется до вызова подпрограммы vaiidateAHBiocks. В качестве параметра она использует любое значение, которое вы хотите передать функциям проверки корректности, зарегистрированным вместе с библиотекой. Раньше при помощи этого параметра я определял глубину проверок корректности, выполняемых функцией. Имейте в виду, что validateAHBlocks пересылает данное значение каждой зарегистрированной подпрограмме проверки корректности, чтобы можно было координировать эти значения в команде разработчиков.
Чтобы понять, как работают функции расширения MemDumperValidator, просмотрите программу с именем Dump, показанную в листинге 15-2. Dump — это "пустая", незаполненная программа, в которой показано все,, что нужно для использования этого расширения. Я не привожу пример кода, но MemDumperValidator хорошо работает и с MFC, потому что MFC будет вызывать любые предварительно зарегистрированные функции подключения клиентских дампов.
Листинг 15-2. DUMP.CPP
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"Debugging Applications" (Microsoft Press)
Copyright (c) 1997-2000 John Robbins — All rights reserved.
- - - - - - - - - - - - - - - - - - - - - - - - - -*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <iostream.h>
#include "BugslayerUtil.h"
class TestClass
{
public:
TestClass ( void)
{
strcpy ( m_szData, "TestClass constructor data!");
}
TestClass ( void)
{
m_szData[ 0 ] = '\0';
}
// Объявление средств отладки памяти для классов языка C++
DECLARE_MEMDEBUG ( TestClass);
private :
char m_szData[ 100 ];
};
// Этот макрос устанавливает статическую структуру DVINFO .
IMPLEMENT_MEMDEBUG ( TestClass);
Listing 15-2DUMP.CPP *
// Методы выдачи дампов и проверки корректности блоков памяти
#ifdef _DEBUG
void TestClass::ClassDumper ( const void * pData)
{
TestClass * pClass = (TestClass*JpData;
_RPT1 ( _CRT_WARN,
" TestClass::ClassDumper : %s\n", pClass->m_szData);
}
void TestClass::ClassValidator ( const void * pData ,
const void * )
{
// Проверка корректности данных.
TestClass * pClass = (TestClass*)pData;
_RPT1 ( _CRT_WARN ,
" TestClass::ClassValidator : %s\n",
pClass->m_szData );
}
#endif
typedef struct tag_SimpleStruct
{
char szNamef 256 ]; char szRank[ 256 ];
}
SimpleStruct;
// Методы выдачи дампов и проверки корректности для памяти,
// содержащей простые строчные данные
void DumperOne ( const void * pData}
{
_RPT1 ( _CRT_WARN, " Data is : %s\n", pData);
}
void ValidatorOne ( const void * pData, const void * pContext)
{
// Проверка корректности строчных данных
. _RPT2 ( _CRT_WARN,
" Validator called with : %s : Ox%08X\n",
pData, pContext);
}
// Методы вьщачи дампов и проверки корректности для структур
void DumperTwo ( const void * pData)
{
_RPT2 ( _CRT_WARN
" Data is Name : %s\n"
" Rank : %s\n" ,
((SimpleStruct*)pData)->szName ,
((SimpleStruct*)pData)->szRank );
}
void ValidatorTwo ( const void * pData, const void * pContext)
{
// Проверка корректности структур.
_RPT2 ( _CRT_WARN , " Val%dator called with :\n"
" Data is Name : %s\n"
" Rank : %s\n" ,
((SimpleStruct*)pData)->szName ,
((SimpleStruct*)pData)->szRank );
}
// К сожалению, функции языка С используют собственные структуры
// DVINFO. Эти структуры необходимо определять как внешние ссылки, а
// для макроса MEMDEBUG нужно создавать собственный макрос-оболочку,
static DVINFO g_dvOne;
static DVINFO g_dvTwo;
void main ( void)
{
cout « "At start of main\n";
// Инициализация отладки памяти для типа One.
INITIALIZE_MEMDEBUG ( &g_dvOne, DumperOne, ValidatorOne) ;
// Инициализация отладки памяти для типа Two.
INITIALIZE_MEMDEBUG ( Sg_dvTwo, DumperTwo, ValidatorTwo) ;
// Распределить память для класса с новым MEMDEBUG.
TestClass * pstClass;
//pstClass = MEMDEBUG_NEW TestClass;
pstClass = new TestClass;
// Распределить память для двух типов языка С.
char * р = (char*)MEMDEBUG_MALLOC '( &g_dvOne, 10);
strcpy ( р, "VC VC");
SimpleStruct * pSt =
(SimpleStruct*)MEMDEBUG_MALLOC ( Sg_dvTwo,
sizeof ( SimpleStruct));
strcpy ( pSt->szName, "Pam");
strcpy ( pSt->szRank, "CINC");
// Проверить корректность всех блоков в списке.
VALIDATEALLBLOCKS ( NULL);
cout « "At end of main\n";
// Дамп каждого блока будет выведен как часть проверки утечки памяти
}