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

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

Шрифт:

// No_default

vector<No_default> v3;

v3.resize(100, No_default(2)); // добавляем 100 копий объектов

// No_default(2)

v3.resize(200); // ошибка: попытка создать 200

// No_default

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

vector
, мы столкнулись с проблемой, которой раньше, как пользователи класса
vector
, не имели.

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

allocator
, распределяющий неинициализированную память. Слегка упрощенный вариант приведен ниже.

template<class T> class allocator {

public:

// ...

T* allocate(int n); // выделяет память для n объектов типа T

void deallocate(T* p, int n); // освобождает память, занятую n

// объектами типа T, начиная с адреса p

void construct(T* p, const T& v); // создает объект типа T

// со значением v по адресу p

void destroy(T* p); // уничтожает объект T по адресу p

};

Если вам нужна полная информация по этому вопросу, обратитесь к книге The C++ Programming Language или к стандарту языка С++ (см. описание заголовка <memory> ), а также к разделу B.1.1. Тем не менее в нашей программе демонстрируются четыре фундаментальных операции, позволяющих выполнять следующие действия:

• Выделение памяти, достаточной для хранения объекта типа

T
без инициализации.

• Создание объекта типа

T
в неинициализированной памяти.

• Уничтожение объекта типа

T
и возвращение памяти в неинициализированное состояние.

• Освобождение неинициализированной памяти, достаточной для хранения объекта типа

T
без инициализации.

Не удивительно, что класс

allocator
— то, что нужно для реализации функции
vector<T>::reserve
. Начнем с того, что включим в класс
vector
параметр класса
allocator
.

template<class T, class A = allocator<T> > class vector {

A alloc; // используем объект класса allocator для работы

// с памятью, выделяемой для элементов

// ...

};

Кроме распределителя памяти, используемого вместо оператора

new
, остальная часть описания класса
vector
не отличается от прежнего. Как пользователи класса
vector
, мы можем игнорировать распределители памяти, пока сами не захотим, чтобы класс
vector
управлял памятью, выделенной для его элементов, нестандартным образом. Как разработчики класса
vector
и как студенты, пытающиеся понять фундаментальные проблемы и освоить основные технологии программирования, мы должны понимать, как вектор работает с неинициализированной памятью, и предоставить пользователям правильно сконструированные объекты. Единственный код, который следует изменить, — это функции-члены класса
vector
, непосредственно работающие с памятью, например функция
vector<T>::reserve
.

template<class T, class A>

void vector<T,A>::reserve(int newalloc)

{

if (newalloc<=space) return; // размер не уменьшается

T* p = alloc.allocate(newalloc); // выделяем новую память

for (int i=0; i<sz; ++i) alloc.construct(&p[i],elem[i]);

// копируем

for (int i=0; i<sz; ++i) alloc.destroy(&elem[i]); // уничтожаем

alloc.deallocate(elem,space); // освобождаем старую память

elem = p;

space = newalloc;

}

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

string
, присваивание подразумевает, что целевая область памяти уже проинициализирована.

Имея функции

reserve
,
vector<T,A>::push_back
, можно без труда написать следующий код.

template<class T, class A>

void vector<T,A>::push_back(const T& val)

{

if (space==0) reserve(8); // начинаем с памяти для 8 элементов

else if (sz==space) reserve(2*space); // выделяем больше памяти

alloc.construct(&elem[sz],val); // добавляем в конец

// значение val

++sz; // увеличиваем размер

  • Читать дальше
  • 1
  • ...
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • ...

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

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

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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