//
//
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
//
//
//
StrBlobPtr begin() { return StrBlobPtr(*this); }
StrBlobPtr end()
{ auto ret = StrBlobPtr(*this, data->size());
return ret; }
};
Упражнение 12.19. Определите собственную версию класса StrBlobPtr и модифицируйте класс StrBlob соответствующим объявлением дружественным, а также функциями-членами begin() и end().
Упражнение 12.20. Напишите программу, которая построчно читает исходный файл в операционной системе класса StrBlob и использует указатель StrBlobPtr для вывода каждого его элемента.
Упражнение 12.21. Функцию-член deref() класса StrBlobPtr можно написать следующим образом:
std::string& deref() const
{ return (*check(curr, "dereference past end"))[curr]; }
Какая версия по-вашему лучше и почему?
Упражнение 12.22. Какие изменения следует внести в класс StrBlobPtr, чтобы получить класс, применимый с типом const StrBlob? Определите класс по имени ConstStrBlobPtr, способный указывать на const StrBlob.
Операторы new и delete резервируют объекты по одному. Некоторым приложениям нужен способ резервировать хранилище для многих объектов сразу. Например, векторы и строки хранят свои элементы в непрерывной памяти и должны резервировать несколько элементов сразу всякий раз, когда контейнеру нужно повторное резервирование (см. раздел 9.4).
Для этого язык и библиотека предоставляют два способа резервирования всего массива объектов. Язык определяет второй вид оператора new, резервирующего и инициализирующего массив объектов. Библиотека предоставляет шаблон класса allocator, позволяющий отделять резервирование от инициализации. По причинам, описанным в разделе 12.2.2, применение класса allocator обычно обеспечивает лучшую производительность и более гибкое управление памятью.
У многих (возможно, у большинства) приложений нет никакой непосредственной необходимости в динамических массивах. Когда приложение нуждается в переменном количестве объектов, практически всегда проще, быстрей и безопасней использовать вектор (или другой библиотечный контейнер), как было сделано в классе StrBlob. По причинам, описанным в разделе 13.6, преимущества использования библиотечного контейнера даже более явны по новому стандарту. Библиотеки, поддерживающие новый стандарт, работают существенно быстрее, чем предыдущие версии.
Как уже упоминалось, использующие контейнеры классы могут использовать заданные по умолчанию версии операторов копирования, присвоения и удаления (см. раздел 7.1.5). Классы, резервирующие динамические массивы, должны определить собственные версии этих операторов для управления памятью при копировании, присвоении и удалении объектов.
new и массивы
Чтобы запросить оператор new зарезервировать массив объектов, после имени типа следует указать в квадратных скобках количество резервируемых объектов. В данном случае оператор new резервирует требуемое количество объектов и (при успешном резервировании) возвращает указатель на первый из них:
//
int *pia = new int[get_size()]; //
Значение в скобках должно иметь целочисленный тип, но не обязано быть константой.
Для представления типа массива при резервировании можно также использовать псевдоним типа (см. раздел 2.5.1). В данном случае скобки не нужны:
typedef int arrT[42]; //
int *p = new arrT; //