Программирование. Принципы и практика использования C++ Исправленное издание, стр. 261
• Как запрограммировать класс
vector<X>X• Как гарантировать, что элементы вектора будут уничтожены в конце работы с ним?
Мы можем работать с типами, не имеющими значений по умолчанию, предоставив пользователю возможность задавать это значение самостоятельно.
template<class T> void vector<T>::resize(int newsize, T def = T());Иначе говоря, используйте в качестве значения по молчанию объект, созданный конструктором
T()vector<double> v1;v1.resize(100); // добавляем 100 копий объекта double(), т.е. 0.0v1.resize(200, 0.0); // добавляем 200 копий числа 0.0 — упоминание // излишнеv1.resize(300, 1.0); // добавляем 300 копий числа 1.0struct No_default { No_default(int); // единственный конструктор класса No_default // ...};vector<No_default> v2(10); // ошибка: попытка создать 10 // No_default()vector<No_default> v3;v3.resize(100, No_default(2)); // добавляем 100 копий объектов // No_default(2)v3.resize(200); // ошибка: попытка создать 200 // No_default()Проблему, связанную с деструктором, устранить труднее. По существу, мы оказались в действительно трудной ситуации: в структуре данных часть данных проинициализирована, а часть — нет. До сих пор мы старались избегать неинициализированных данных и ошибок, которые ими порождаются. Теперь, как разработчики класса
vectorvectorВо-первых, мы должны найти способ для получения неинициализированной памяти и манипулирования ею. К счастью, стандартная библиотека содержит класс
allocatortemplate<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Не удивительно, что класс
allocatorvector<T>::reserve()vectorallocatortemplate<class T, class A = allocator<T> > class vector { A alloc; // используем объект класса allocator для работы // с памятью, выделяемой для элементов // ...};Кроме распределителя памяти, используемого вместо оператора
newvectorvectorvectorvectorvectorvector<T>::reserve()template<class T, class A>void vector<T,A>::reserve(int newalloc)