С формальной точки зрения вызов fill(buf,'x')
является сокращенной формой записи fill
, а fill(b2,0)
— сокращение вызова fill
, но, к счастью, мы не всегда обязаны быть такими конкретными. Компилятор сам извлекает эту информацию за нас.
19.3.6. Обобщение класса vector
Когда мы создавали обобщенный класс vector
на основе класса “vector
элементов типа double
” и вывели шаблон “vector
элементов типа T
”, мы не проверяли определения функций push_back()
, resize()
и reserve()
. Теперь мы обязаны это сделать, поскольку в разделах 19.2.2 и 19.2.3 эти функции были определены на основе предположений, которые были справедливы для типа double
, но не выполняются для всех типов, которые мы хотели бы использовать как тип элементов вектора.
• Как запрограммировать класс vector
, если тип X
не имеет значения по умолчанию?
• Как гарантировать, что элементы вектора будут уничтожены в конце работы с ним?
Мы можем работать с типами, не имеющими значений по умолчанию, предоставив пользователю возможность задавать это значение самостоятельно.
template
Иначе говоря, используйте в качестве значения по молчанию объект, созданный конструктором T()
, если пользователь не указал иначе. Рассмотрим пример.
vector
v1.resize(100); // добавляем 100 копий объекта double(), т.е. 0.0
v1.resize(200, 0.0); // добавляем 200 копий числа 0.0 — упоминание
// излишне
v1.resize(300, 1.0); // добавляем 300 копий числа 1.0
struct No_default {
No_default(int); // единственный конструктор класса No_default
// ...
};
vector
// No_default()
vector
v3.resize(100, No_default(2)); // добавляем 100 копий объектов
// No_default(2)
v3.resize(200); // ошибка: попытка создать 200
// No_default()
Проблему, связанную с деструктором, устранить труднее. По существу, мы оказались в действительно трудной ситуации: в структуре данных часть данных проинициализирована, а часть — нет. До сих пор мы старались избегать неинициализированных данных и ошибок, которые ими порождаются. Теперь, как разработчики класса vector
, мы столкнулись с проблемой, которой раньше, как пользователи класса vector
, не имели.
Во-первых, мы должны найти способ для получения неинициализированной памяти и манипулирования ею. К счастью, стандартная библиотека содержит класс allocator
, распределяющий неинициализированную память. Слегка упрощенный вариант приведен ниже.
template
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
};
Если вам нужна полная информация по этому вопросу, обратитесь к книге