Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 67 68 69 [ 70 ] 71 72 73 ... 144


сравнимо с аналогичной асимметрией между конструкторами и деструкторами. Таким образом, при создании объекта вы .можете выбрать, скажем, один из четырех распределителей памяти и один из пяти конструкторов, но во время уничтожения объекта в вашем распоряжении будет лишь один способ:

delete р;

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

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

class X { . . .

virtual -ХО ;

class Y : public X { . .. -Y () ;

void f(X* pi) {

X* p2 = new Y;

delete p2; вызывается Y-.-.-Y delete pi; вызывается правильный деструктор, каким бы он ни оказался

Гарантируется также, что если в иерархии классов определены локальные операторы operator delete (), то будет вызван нужный. Если бы виртуальный деструктор не использовался, то действия по очистке, заданные в деструкторе класса Y, никогда не выполнялись бы.

Однако для выбора функции освобождения памяти в языке нет средств, парных механизму выбора функции распределения:

class X { . . .

void* operator new(size t); обычное выделение памяти

void* operator new(size t, Arena&); из арены Arena

void operator delete(void*);

нельзя определить void operator delete (void*, ArenaSc) ;

Ведь нельзя предполагать, что в точке уничтожения объекта известно, как он был создан. Идеальный вариант - тот, при котором пользователю вообше не нужно освобождать па.мять, занятую объектом. Для этого, в частности, применяются



специальные арены. Арену можно определить так, что в некоторой известной точке программы будет освобождаться вся память, занятая этой программой. Можно также написать для нее сборщик мусора. Первый подход очень распространен, второй - нет. Специальный сборщик мусора должен быть написан удачно, иначе nponie встроить в программу стандартный [Boehm, 1993].

Чаще при программировании функций operator new () оставляют некоторый признак, который доступен при вызове operator delete () и пока.зывает, как следует освобождать память. Заметим, что все это относится к управлению па.мятью, следовательно, находится на более низком уровне, чем создание объектов с помощью конструкторов и их уничтожение деструкторами. Поэтому область памяти, где хранится такой признак, не является частью объекта, но как-то связана с ним. Например, operator new () мог бы поместить информацию, относящуюся к управлению памятью, в слово, предществуюп1ее то.му, иа которое указывает возвращениое и.м значение. Также operator new() .мог бы оставить эту информацию там, где конструкторы или другие функции имеют воз.можность найти ее и выяснить, распределена ли память для объекта из кучи.

Был ли запрет на перегрузку delete () ошибкой? Не уверен, но ду.маю, что .этот случай из разряда тех, где любое решение порождает проблемы.

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

10.5.1. Освобождение памяти для массивов

В С++ единичный объект, обозначегтый указателем, вполне может оказаться первым элементо.м массива. Так же было и в С. Если указатель указывает на первый элемент массива, то обычно говорят, что он указывает на массив. Обычно компилятор ие .может отличить указатели на единичный объект и на массив. Выделение и освобождение памяти для массивов производится именно через такие указатели. Напри.мер:

void f(X* pi) pi может указывать на единичный объект или на массив

X* р2 = new Х[10]; р2 указывает на массив . . .

Как можно гарантировать правильное удаление массива? В частности, как гарантировать, что для каждого элемента массива вызван деструктор? В версии 1.0 не было удовлетворительного ответа на эти вопросы. В версии 2.0 появился оператор удаления массива delete [ ]:

void f(X* pi) pi может указывать на единичный объект или на массив

X* р2 = new Х[10]; р2 указывает на массив . . .

delete р2; ошибка: р2 указывает на массив



delete[] р2; правильно

delete р1; может, и правильно

delete[] р1; может, и правильно

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

В промежуточном варианте delete [] програ.ммист должен был указывать число элементов в массиве, например:

delete[10] р2;

Это слишком часто приводило к ошибка.м, поэтому за хранение информации о числе элементов в массиве отвечал компилятор.

10.6. Нехватка памяти

Невоз.можность получить запрошенный ресурс - распространенная проблема. Еще до появления версии 2.0 стало понятно, что ее решение следует искать в области обработки исключений (см. раздел 3.15). Однако до обработки исключений в общем виде (см. главу 16) было еще далеко, а конкретную проблему нехватки па.мяти следовало решать немедленно. Для переходного периода, растянувшегося на несколько лет, нужно было предложить хоть какое-то решение.

Самыми животрепещущими являлись две проблемы:

□ пользователю следует передать управление во всех случаях, когда вызов библиотечной функции завершается неудачей из-за нехватки памяти (или по любой другой причине). Это непререкаемое требование исходило от пользователей, работающих в AT&T;

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

Первое требование было удовлетворено тем, что при нехватке памяти конструктор не вызывался, а operator new () возвращал 0. В .этом случае выражение new также принимало значение О, а важные приложения оказывались защищенными от нехватки памяти. Например:

void f() {

X* р = new X; if (Р == 0) {

обработать ошибку выделения памяти

конструктор не вызывается

использовать р



1 ... 67 68 69 [ 70 ] 71 72 73 ... 144

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