Динамически распределяемая память

Автор работы: Пользователь скрыл имя, 26 Октября 2010 в 22:57, Не определен

Описание работы

Реферат

Файлы: 1 файл

Динамически распределяемая память.doc

— 265.50 Кб (Скачать файл)

         void* p = HeapAlloc(s hHeap, 0, size);  

         if (p != NULL)

         {  

        // память  выделена успешно; увеличиваем  счетчик объектов CSomeClass в куче  

           s_uNumAllocsInHeap++;  

         }  

         // возвращаем адрес созданного  объекта CSomeClass

         return(p);  

     }  

Заметьте, что  сначала я объявил два статических элемента данных, s_hHeap и s_uNumAllocsInHeap, а затем инициализировал их значениями NULL и 0 соответственно.  

Оператор new принимает  один параметр — size, указывающий число  байтов, нуж ных для хранения CSomeClass Первым делом он создает кучу, если таковой нет Для проверки анализируется значение переменной s_bHeap: если оно NULL, кучи нет, и тогда она создается функцией HeapCreate, а описатель, возвращаемый функцией, со храняется в переменной s_bHeap, чтобы при следующем вызове оператора new ис пользовать существующую кучу, а не создавать еще одну.  

Вызывая HeapCreate, я указал флаг HEAP_NO_SERIALIZE, потому что  данная про грамма построена как  однопоточная. Остальные параметры, указанные при вызове HeapCreate, определяют начальный и максимальный размер кучи. Я подставил на их место по нулю. Первый нуль означает, что у кучи нет начального размера, второй — что куча должна расширяться по мере необходимости.  

Hе исключено,  что Вам показалось, будто параметр size оператора new стоит пе редать в HeapCreatc как второй параметр. Вроде бы тогда можно инициализировать кучу так, чтобы она была достаточно большой для размещения одного экземпляра класса. И в таком случае функция HeapAlloc при первом вызове работала бы быстрее, так как не пришлось бы изменять размер кучи под экземпляр класса. Увы, мир устро ен не так, как хотелось бы. Из-за того, что с каждым выделенным внутри кучи блоком памяти связан свой заголовок, при вызове HeapAlloc все равно пришлось бы менять размер кучи, чтобы в нее поместился не только экземпляр класса, но и связанный с ним загловок.  

После создания кучи из нее можно выделять память под новые объекты CSomeClass с помощью  функции HeapAlloc. Первый параметр — описатель  кучи, второй — раз мер объекта CSomeClass. Функция возвращает адрес выделенного блока.  

Если выделение  прошло успешно, я увеличиваю переменную-счетчик s_uNum AllocsInHeap, чтобы знать число  выделенных блоков в куче. Наконец, оператор new возвращает адрес только что созданного объекта CSomeClass.  

Вот так происходит создание нового объекта CSomeClasb. Теперь рассмотрим, как этот объект разрушается, — если он больше не нужен программе. Эта задача возлага ется на функцию, переопределяющую оператор delete.  

     void CSomeClass::operator delete (void* p)

     {  

         if (HeapFrce(s_hHcap, 0, p))

         {  

           // объект удален успешно

           s_uNumAllocsInKeap--;  

         }  

         if (s_uNumAllocsInHeap == 0)

         {  

           // если в куче больше нет объектов, уничтожаем ее 

           if (HeapDestroy(s_hHeap))

           {  

             // описатель кучи приравниваем NULL, чтобы оператор new

             // мог создать новую кучу при создании нового объекта

             CSomeClass s_hHeap = NULL;  

           }  

         }  

     }  

Оператор delete принимает  только один параметр: адрес удаляемого объекта. Сна чала он вызывает HeapFree и передает ей описатель кучи и  адрес высвобождаемого объекта. Если объект освобожден успешно, s_uNumAllocslnHeap уменьшается, показы вая, что одним объектом CSomeClass в куче стало меньше. Далее оператор проверяет: не равна ли эта переменная 0, и, если да, вызывает HeapDestroy, передавая ей описа тель кучи. Если куча уничтожена, s_hHeap присваивается NULL. Это важно: ведь в бу дущем наша программа может попытаться создать другой объект CSomeClass. При этом будет вызван оператор new, который проверит значение s_hHeap, чтобы опре делить, нужно ли использовать существующую кучу или создать новую.  

Данный пример иллюстрируеn очень удобную схему  работы с несколькими куча ми. Этот код легко подстроить и включить в Ваши классы. Но сначала, может  быть, стоит поразмыслить над проблемой  наследования. Если при создании нового класса Вы используете класс CSomeClass как базовый, то производный класс унаследует опе раторы new и delete, принадлежащие классу CSomeClass. Новый класс унаследует и его кучу, а это значит, что применение оператора new к производному классу повлечет выделение памяти для объекта этого класса из той же кучи, которую использует и класс CSomeClass. Хорошо это или нет, зависит от конкретной ситуации. Если объек ты сильно различаются размерами, это может привести к фрагментации кучи, что зятруднит выявление таких ошибок в коде, о которых я рассказывал в разделах «За щита компонентов» и «Более эффективное управление памятью».  

Если Вы хотите использовать отдельную кучу для  производных классов, нужно продублировать все, что я сделал для класса CSomeClass. Л конкретнее - включить еще один набор переменных s_hHeap и s_uNumAllocsInHeap и повторить еще раз код для операторов new и delete. Компилятор увидит, что Вы переопределили в производном классе операторы new и delete, и сформирует обращение именно к ним, а не к тем, которые содержатся в базовом классе.  

Если Вы не будете создавать отдельные кучи для  каждого класса, то получите един ственное преимущество: Вам не придется выделять память под каждую кучу и соот ветствующие  заголовки. Но кучи и заголовки не занимают значительных объемов  

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

Другие функции  управления кучами  

Кроме уже упомянутых, в Windows есть еще несколько функций, предназначенных для управления кучами. В этом рязделс я вкратце  расскажу Вам о них.  

ToolHelp-функции  (упомянутые в конце главы 4) дают возможность перечислять кучи процесса, а также выделенные внутри них блоки памяти. За более подробной информацией я отсылаю Вас к документации Plarform SDK: ищите разделы по функ циям Heap32Ftrst, Heap32Next, Heap32ListFirst и Heap32ListNext. Самое ценное в этих функциях то, что они доступны как в Windows 98, так и в Windows 2000. Прочие функ ции, о которых пойдет речь в этом разделе, есть только в Windows 2000.  

В адресном пространстве процесса может быть несколько куч, и функция GetPro cessHeaps позволяет получить их описатели "одним махом":  

     DWORD GetProcessHeaps( DWORD dwNumHeaps, PHANDLE pHeaps);  

Предварительно  Вы должны создать массив описателей, а затем вызвать функцию так, как показано ниже.  

     HANDLE hHeaps[25];  

     DWORD dwHeaps = GetProcessHeaps(25, hHeaps);  

     if (dwHeaps > 25)

     {  

         // у процесса больше куч, чем  мы ожидали  

     }

     else

     {  

         // элеметы от hHeaps[0] до hHeaps[dwHeaps - 1]

         // идентифицируют существующие кучи  

     }  

Имейте в виду, что описатель стандартной кучи процесса тоже включается в этот массив описателей, возвращаемый функцией GetProcessHeaps. Целостность кучи позволяет проверить функция HeapValidate:  

     BOOL HeapValidate( HANDLE hHeap, DWORD fdwFlags, LPCVOID pvMem);  

Обычно ее вызывают, передавая в hHeap описатель кучи, в dwFlags — 0 (этот па раметр допускает еще флаг HEAP_NO_SERIALIZE), а в pvMem — NULL. Функция про сматривает все блоки в куче, чтобы убедиться в отсутствии поврежденных блоков. Чтобы она работала быстрее, в параметре pvMem можно передать адрес конкретного блока, Тогда функция проверит только этот блок.  

Для объединения  свободных блоков в куче, а также  для возврата системе любых страниц  памяти, на которых нет выделенных блоков, предназначена функция Heap Compact:  

     UINT HeapCompact( HANDLE hHeap, DWORD fdwFlags);  

Обычно в параметре fdwFlags передают 0, но можно передать и HEAP_NO_SE RIALIZE  

Следующие две  функции — HeapLock и HeapUnlock — используются парно  

     BOOL Hpaplock(HANDLE hHeap);

     BOOL HeapUnlock(HANDLE hHeap);  

Они предназначены  для синхронизации потоков После успешного вызова Heap Lock поток, который вьзывал эту функцию становится владельцем указанной кучи Ьсли другой поток обращается к этой куче, указывая тот же описатель кучи, система приостанавливает его выполнение до тех пор, пока куча не будет разблокирована вызовом HeapUnlock  

Функции HeapAlloc, HeapSize, HeapFree и другие — все обращаются к HeapLock и HeapUnlock, чтобы обеспечить последовательный доступ к куче Самостоятельно вы зывать эти функции Вам вряд ли понадобится  

Последняя функция, предназначенная для работы с кучами, — HeapWalk  

     BOOL HeapWalk( HANDLE hHeap, PPROCESS_HEAP_ENTRY pHoapEntry);  

Она предназначена  только для отладки и позволяет  просматривать содержимое кучи Обычно ее вызывают по несколько paз, передавая адрес структуры PROCESS_ HEAP_ENTRY (Вы должны сами создать ее экземпляр и инициализировать)  

typedef struct _PROCESS_HEAP_ENTRY

{  

     PVOID lpData;

     DWORD cbData;

     BYlE cbOverhead;

     BYTE iRegionIndex;

     WORD wFlags;  

     union

     {  

         struct

         {  

           HANDLE hMem; DWORD dwReserved[ 3 ];  

         } Block;  

         struct

         {  

           DWORD dwCornmittedSize;

           DWORD dwUnCommittedSize;

           LPVOID lpFirstBlock;

           LPVOID lpLastBlock;  

         } Region;  

     };  

} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY, *PPROCESS_HEAP_ENTRY;  

Прежде чем  перечислять блоки в куче, присвойте NULL элементу lpData, и это заставит функцию HeapWalk инициализировать все элементы структуры Чтобы пе рейти к следующему блоку, вызовите HeapWalk еще раз, переддв сй тот же описатель кучи и адрес той же структуры PROCESS_HFAPENTRY Если HeapWalk вернет FALSE, значит, блоков в куче больше нет Подробное описание элементов структуры PRO CESS_HEAP_ENTRY см. в документации PlatformSDK  

Обычно вызовы функции HeapWalk "обрамляют" вызовами HeapLock и HeapUnlock, чтобы посторонние  потоки не портили картину, создавая или удаляя блоки в просматриваемой куче

Информация о работе Динамически распределяемая память