Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

Обе проблемы встречаются в реальных программах; так что это не просто теоретические возможности.

Удаление нулевого указателя не приводит ни к каким последствиям (так как нулевой указатель не ссылается ни на один объект), поэтому эта операция безвредна. Рассмотрим пример.

int* p = 0;

delete p; // отлично: никаких действий не нужно

delete p; // тоже хорошо (по-прежнему ничего делать не нужно)

Зачем возиться с освобождением памяти? Разве компилятор сам не может понять, когда память нам больше не нужна, и освободить ее без вмешательства человека? Может. Такой механизм называется автоматической сборкой мусора (automatic garbage collection) или просто сборкой мусора (garbage collection). К сожалению, автоматическая сборка мусора недешевое удовольствие и не идеально подходит ко всем приложениям. Если вам действительно нужна автоматическая сборка мусора, можете встроить этот механизм в свою программу. Хорошие сборщики мусора доступны по адресу www.research.att.com/~bs/C++.html. Однако в этой книге мы предполагаем, что читатели сами разберутся со своим “мусором”, а мы покажем, как это сделать удобно и эффективно.

Почему следует избегать утечки памяти? Программа, которая должна работать бесконечно, не должна допускать никаких утечек памяти. Примером таких программ является операционная система, а также большинство встроенных систем (о них речь пойдет в главе 25). Библиотеки также не должны допускать утечек памяти, поскольку кто-нибудь может использовать эти библиотеки как часть системы, работающей бесконечно. В общем, утечек памяти следует избегать, и все тут. Многие программисты рассматривают утечки памяти как проявление неряшливости. Однако эта точка зрения кажется нам слишком категоричной. Если программа выполняется под управлением операционной системы (Unix, Windows или какой-нибудь еще), то после завершения работы программы вся память будет автоматически возвращена системе. Отсюда следует, что если вам известно, что ваша программа не будет использовать больше памяти, чем ей доступно, то вполне можно допустить утечку, пока операционная система сама не восстановит порядок. Тем не менее, если вы решитесь на это, то надо быть уверенным в том, что ваша оценка объема используемой памяти является правильной, иначе вас сочтут неряхой.

17.5. Деструкторы

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

// очень упрощенный вектор, содержащий числа типа double

class vector {

int sz; // размер

double* elem; // указатель на элементы

public:

vector(int s) // конструктор

:sz(s), // инициализация члена sz

elem(new double[s]) // инициализация члена elem

{

for (int i=0; i<s; ++i) elem[i]=0; // инициализация

// элементов

}

int size const { return sz; } // текущий размер

// ...

};

Итак, член

sz
хранит количество элементов. Мы инициализируем его в конструкторе, а пользователь класса
vector
может выяснить количество элементов, вызвав функцию
size
. Память для элементов выделяется в конструкторе с помощью оператора
new
, а указатель, возвращенный оператором
new
, хранится в члене
elem
.

Обратите внимание на то, что мы инициализируем элементы их значением по умолчанию (

0.0
). Класс
vector
из стандартной библиотеки делает именно так, поэтому мы решили сделать так же с самого начала.

К сожалению, наш примитивный класс

vector
допускает утечку памяти. В конструкторе он выделяет память для элементов с помощью оператора
new
. Следуя правилу, сформулированному в разделе 17.4, мы должны освободить эту память с помощью оператора
delete
. Рассмотрим пример.

void f(int n)

{

vector v(n); // выделяем память для n чисел типа double

// ...

}

После выхода из функции

f
элементы вектора
v
, созданные в свободной памяти, не удаляются. Мы могли бы определить функцию
clean_up
— член класса
vector
и вызвать ее следующим образом:

void f2(int n)

{

vector v(n); // определяем вектор,

// выделяющий память для других n переменных

// типа int

// ...используем вектор v...

v.clean_up; // функция clean_up удаляет член elem

}

Этот подход должен был бы сработать. Однако одна из наиболее распространенных проблем, связанных со свободной памятью, заключается в том, что люди забывают об операторе delete. Эквивалентная проблема может возникнуть и с функцией
clean_up
; люди просто забудут ее вызвать. Мы можем предложить более удачное решение. Основная идея состоит в том, чтобы компилятор знал не только о конструкторе, но и о функции, играющей роль, противоположную конструктору. Такую функцию логично назвать деструктором (destructor). Точно так же как конструктор неявно вызывается при создании объекта класса, деструктор неявно вызывается, когда объект выходит за пределы области видимости. Конструктор гарантирует, что объект будет правильно создан и проинициализирован. Деструктор, наоборот, гарантирует, что объект будет правильно очищен перед тем, как будет уничтожен. Рассмотрим пример.

  • Читать дальше
  • 1
  • ...
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: