Отладка приложений

       

Инициализация и завершение в C++


Закончив реализацию расширения MemDumperValidator и начав его тестировать, я убедился, что расширение работало так, как было запланировано. Однако, обдумывая все способы, с помощью которых программа может распределять память в куче, и просматривая код MemDumperValidator, я обнаружил явный пробел в его логике: отсутствовали статические конструкторы, распределяющие память.

Хотя большинство разработчиков не очень часто используют этот прием, в некоторых случаях память распределяется перед точкой входа приложения.

При моем подходе к разработке MemDumperValidator проблема состояла в следующем: нужно было гарантировать, что установки подходящих флажков для функции _CrtsetDbgFiag будут выполняться прежде, чем произойдут какие-либо распределения памяти.

И, наконец, последний момент, который надо подчеркнуть в связи с расширением MemDumperValidator: перед началом использования DCRT-биб-лиотеки не забывайте вызывать какую-нибудь, хотя бы самую простую, функцию инициализации. В С-программах не очень удобно иметь дело со структурами DVINFO. Я хотел сделать MemDumperValidator настолько автоматическим, насколько это возможно, чтобы применение его разработчиками не вызывало каких-либо затруднений.

К счастью, существует директива #pragma init_seg, позволяющая управлять инициализацией и порядком ликвидации статически объявленных значений. Директиве #pragma init_seg можно передать один из следующих параметров, которые, no-существу, специфицируют тип инициализируемого значения: compiler, lib, user, section name И funcname. Наиболее важными ЯВЛЯЮТСЯ три первых параметра.

Параметр compiler зарезервирован для компилятора Microsoft. Любые объекты, маркированные меткой compiler, создаются первыми, а разрушаются последними. Объекты, маркированные как lib, создаются после и разрушаются перед объектами compiler, а объекты, маркированные меткой user, строятся последними, а разрушаются первыми.

 Напомним, что в языке C++ созданием и разрушением объектов в динамической памяти занимаются специальные методы — конструкторы и деструкторы.
Первые занимаются выделением памяти и инициализацией объекта, а вторые — освобождением выделенной объекту памяти, т. е. его ликвидацией, разрушением. — Пер

Поскольку код расширения MemDumperValidator должен быть инициализирован перед тем, как будет инициализирован ваш код, можно просто передать lib как параметр директиве #pragma init_seg и на этом закончить работу. Однако если вы создаете библиотеки и маркируете их в виде lib-сегментов (как и должно быть) и хотите использовать мой код, то его нужно инициализировать перед инициализацией вашего кода. Для этого данной директиве надо передать параметр: #pragma init_seg (compiler). Хотя нужно всегда следовать правилам, приводящим к правильной инициализации кодовых сегментов, применение параметра compiler в отладочном коде оказывается достаточно безопасным занятием.

Поскольку идеи инициализации работают только в кодах языка C++, в MemDumperValidator включен специальный статический класс (с именем AutoMatic), который просто вызывает функцию _CrtSetDbgFiag. Приходится идти на все эти ухищрения только потому, что это единственный способ установить DCRT-флажки перед инициализацией других библиотек. Кроме того, чтобы обойти некоторые ограничения при проверке утечек памяти в DCRT-библиотеке, необходимо выполнять некоторую специальную обработку при разрушении объектов соответствующего класса. Даже если бы MemDumperValidator имел только С-интерфейс, то всегда можно было бы использовать преимущества языка C++ для его установки и запуска, чтобы он всегда был готов для вызова.



Содержание раздела