Программирование >>  Разработка устойчивых систем 

1 ... 18 19 20 [ 21 ] 22 23 24 ... 196


Спасибо Pery Чарни (Reg Chamey) из Комитета по стандартизации С++, предложившему этот прием.

Чтобы использовать систему проверки памяти, включите заголовочный файл MemCheck.h, скомпонуйте файл MemCheck.obj со своим приложением для перехвата всех вызовов new и delete и активизируйте трассировку памяти макросом MEM ON() (см. далее). Информация обо всех операциях выделения и освобождения памяти будет направляться в стандартный выходной поток (через stdout). При использовании этой системы для всех вызовов new сохраняется имя файла и номер строки, содержащей вызов. Для этого мы задействуем синтаксис размещения оператора new. Хотя обычно синтаксис размещения применяется в тех случаях, когда объекты должны находиться по определенному адресу памяти, он также позволяет создать функцию operator new() с любым количеством аргументов. В следующем примере дополнительные аргументы используются для сохранения результатов макросов FILE и LINE при каждом вызове new:

: С02:MemCheck.h #1fndef МЕМСНЕСК Н Idefine МЕМСНЕСК Н #1nclude <cstddef> для size t

Перехват оператора new (в версиях для скаляров и массивов) void* operator newCstd::size t. const char*, long): void* operator new[](std::size t. const char*, long): , #define new new ( FILE . LINE )

extern bool traceFlag:

#define TRACE ON() traceFlag = true

#define TRACE OFF() traceFlag = false

extern bool activeFlag:

#define MEM ON() activeFlag - true

#define MEM OFF() activeFlag = false

#endif MEMCHECK H /:-

Этот файл должен включаться во все исходные файлы, для которых требуется отслеживать операции с кучей, но включаться он должен обязательно последним (после всех остальных директив #include). Большинство заголовочных файлов стандартной библиотеки содержит шаблоны, а поскольку многие компиляторы используют при компиляции шаблонов модель с включением, макрос, заменяющий new в MemCheck.h, узурпирует все вхождения оператора new в исходном коде библиотеки (что, скорее всего, приведет к ошибкам компиляции). Кроме того, нас интересуют наши собственные ошибки в нашем коде, а не в библиотеках.

В следующем файле, содержащем реализацию системы отслеживания операций с памятью, весь ввод-вывод осуществляется стандартными средствами С (вместо потоков С++). В принципе, это должно быть несущественно, поскольку мы не мешаем потокам ввода-вывода работать с кучей, но некоторые компиляторы были против. С другой стороны, версия <cstdio> никаких нареканий не вызывала.

: C02:MemCheck.cpp {0} #include <cstdio> #i nclude <cstdlib> #include <cassert> #i nclude <cstddef> using namespace std:



Статический фиктивный объект Sentinel s;

} Конец анонимного пространства имен

Перегрузка скалярной версии new void*

operator new(size t siz. const char* file, long line) void* p = malloc(siz); if (activePlag) {

#undef new

Глобальные флаги, устанавливаемые макросами MemCheck.h bool traceFlag = true; bool activePlag = false:

namespace {

Тип записи в карте памяти struct Info {

void* ptr:

const char* file:

long line;

Данные карты памяти const size t MAXPTRS = lOOOOu; Info memMap[MAXPTRS]; size t nptrs = 0;

Поиск адреса в карте int findPtr(void* p) { for (int i =0: i < nptrs: ++i) if (memMap[i].ptr == p) return i; return -1;

void delPtrCvoid* p) { int pos = findPtr(p): assert(p >= 0):

Удаление указателя из карты

for (size t i = pos: i < nptrs-l; ++i)

memMap[i] = memMap[i+l]: --nptrs;

Фиктивный тип для статического деструктора struct Sentinel { -Sentinel О { if (nptrs > 0) { printf( Leaked memory at:\n ): for (size t i = 0: i < nptrs: ++i) pnntf( \t3:p (file: s, line ld)\n . memMap[i].ptr. memMap[i].file. memMap[i].line):

else

printf( No user memory leaks!\n ):



if (nptrs == MAXPTRS) { pr1ntf( memory map too small (increase MAXPTRS)\n ): exit(l):

memMap[nptrs].ptr = p: memMap[nptrs].file = file; memMap[nptrs].line = line; ++nptrs;

if (traceFlag) ( printf( Allocated bytes at address p . siz. p); printf( (file: s. line: ld)\n . file, line):

return p:

Перегрузка версии new для массивов

void* operator new[](size t siz. const char* file, long line) { return operator new(siz. file, line):

Перегрузка скалярной версии delete void operator delete(void* p) { if (findPtr(p) >= 0) ( free(p):

assert(nptrs > 0): delPtr(p); if (traceFlag) printf( Deleted memory at address p\n . p);

else if (!p && activeFlag) printf( Attempt to delete unknown pointer: p\n . p):

Перегрузка версии delete для массива void operator delete[](void* p) {

operator delete(p): } III:-

Логические флаги traceFlag и activeFlag являются глобальными, поэтому их состояние может изменяться в программе макросами TRACE ON(),TRACE OFF(), MEM ON() и MEM OFF(). Обычно в пару макросов MEM ON()-MEM OFF() заключается весь код main(), чтобы операции с памятью отслеживались всегда. Трассировка, выводящая информацию о работе заменителей operator new() и operator delete(), включена по умолчанию, но ее можно отключить макросом TRACE OFF(). В любом случае итоговый результат выводится всегда (см. примеры тестов далее в этой главе).

Система MemCheck отслеживает операции с памятью, для чего все адреса, выделенные оператором new, сохраняются в массиве структур Info. В массиве также хранятся имена файлов и номера строк, в которых находился вызов new. Чтобы предотвратить конфликты с именами глобального пространства имен, мы стараемся сохранить как можно больше информации в анонимном пространстве. Класс Sentinel существует только для вызова деструктора статического объекта при за-верщении программы. Деструктор просматривает тетМар и смотрит, остались ли в карте памяти неудаленные указатели (что является признаком утечки памяти).

Наша функция operator new() получает память с помощью функции malloc(), после чего включает указатель и связанные с ним данные в тетМар. Функция operator delete() отменяет эти операции, вызывая free() и уменьшая nptrs, но сначала она проверяет.



1 ... 18 19 20 [ 21 ] 22 23 24 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика