Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 157 158 159 160 161 162 163 164 165 ... 297
Перейти на страницу:

Поскольку объект может больше не существовать, нельзя использовать указатель weak_ptr для непосредственного доступа к его объекту. Для этого следует вызвать функцию lock(). Она проверяет существование объекта, на который указывает указатель weak_ptr. Если это так, то функция lock() возвращает указатель shared_ptr на совместно используемый объект. Такой указатель гарантирует существование объекта, на который он указывает, по крайней мере, пока существует этот указатель shared_ptr. Рассмотрим пример:

if (shared_ptr<int> np = wp.lock()) { // true, если np не нулевой

 // в if, np совместно использует свой объект с p

}

Внутренняя часть оператора if доступна только в случае истинности вызова функции lock(). В операторе if использование указателя np для доступа к объекту вполне безопасно.

Проверяемый класс указателя

Для того чтобы проиллюстрировать, насколько полезен указатель weak_ptr, определим вспомогательный класс указателя для нашего класса StrBlob. Класс указателя, назовем его StrBlobPtr, будет хранить указатель weak_ptr на переменную-член data класса StrBlob, которым он был инициализирован. Использование указателя weak_ptr не влияет на продолжительность существования вектора, на который указывает данный объект класса StrBlob. Но можно воспрепятствовать попытке доступа к вектору, которого больше не существует.

Класс StrBlobPtr будет иметь две переменные-члена: указатель wptr, который может быть либо нулевым, либо указателем на вектор в объекте класса StrBlob; и переменную curr, хранящую индекс элемента, который в настоящее время обозначает этот объект. Подобно вспомогательному классу класса StrBlob, у класса указателя есть функция-член check(), проверяющая безопасность обращения к значению StrBlobPtr:

// StrBlobPtr передает исключение при попытке доступа к

// несуществующему элементу

class StrBlobPtr {

 public:

 StrBlobPtr() : curr(0) { }

 StrBlobPtr(StrBlob &a, size_t sz = 0):

  wptr(a.data), curr(sz) { }

 std::string& deref() const;

 StrBlobPtr& incr(); // префиксная версия

private:

 // check() возвращает shared_ptr на вектор, если проверка успешна

 std::shared_ptr<std::vector<std::string>>

  check(std::size_t, const std::string&) const;

 // хранит weak_ptr, означая возможность удаления основного вектора

 std::weak_ptr<std::vector<std::string>> wptr;

 std::size_t curr; // текущая позиция в пределах массива

};

Стандартный конструктор создает нулевой указатель StrBlobPtr. Список инициализации его конструктора (см. раздел 7.1.4) явно инициализирует переменную-член curr нулем и неявно инициализирует указатель-член wptr как нулевой указатель weak_ptr. Второй конструктор получает ссылку на StrBlob и (необязательно) значение индекса. Этот конструктор инициализирует wptr как указатель на вектор данного объекта класса StrBlob и инициализирует переменную curr значением sz. Используем аргумент по умолчанию (см. раздел 6.5.1) для инициализации переменной curr, чтобы обозначить первый элемент. Как будет продемонстрировано, ниже параметр sz будет использован функцией-членом end() класса StrBlob.

Следует заметить, что нельзя связать указатель StrBlobPtr с константным объектом класса StrBlob. Это ограничение следует из того факта, что конструктор получает ссылку на неконстантный объект типа StrBlob.

Функция-член check() класса StrBlobPtr отличается от таковой у класса StrBlob, поскольку она должна проверять, существует ли еще вектор, на который он указывает:

std::shared_ptr<std::vector<std::string>>

StrBlobPtr::check(std::size_t i, const std::string &msg) const {

 auto ret = wptr.lock(); // существует ли еще вектор?

 if (!ret)

  throw std::runtime_error("unbound StrBlobPtr");

 if (i >= ret->size())

  throw std::out_of_range(msg);

 return ret; // в противном случае, возвратить shared_ptr на вектор

}

Так как указатель weak_ptr не влияет на счетчик ссылок соответствующего указателя shared_ptr, вектор, на который указывает StrBlobPtr, может быть удален. Если вектора нет, функция lock() возвратит нулевой указатель. В таком случае любое обращение к вектору потерпит неудачу и приведет к передаче исключения. В противном случае функция check() проверит переданный индекс. Если значение допустимо, функция check() возвратит указатель shared_ptr, полученный из функции lock().

Операции с указателями

Определение собственных операторов рассматривается в главе 14, а пока определим функции deref() и incr() для обращения к значению и инкремента указателя класса StrBlobPtr соответственно.

Функция-член deref() вызывает функцию check() для проверки безопасности использования вектора и принадлежности индекса curr его диапазону:

std::string& StrBlobPtr::deref() const {

 auto p = check(curr, "dereference past end");

 return (*p)[curr]; // (*p) - вектор, на который указывает этот объект

}

Если проверка прошла успешно, то p будет указателем типа shared_ptr на вектор, на который указывает данный указатель StrBlobPtr. Выражение (*p)[curr] обращается к значению данного указателя shared_ptr, чтобы получить вектор, и использует оператор индексирования для доступа и возвращения элемента по индексу curr.

Функция-член incr() также вызывает функцию check():

// префикс: возвратить ссылку на объект после инкремента

StrBlobPtr& StrBlobPtr::incr() {

 // если curr уже указывает на элемент после конца контейнера,

 // его инкремент не нужен

 check(curr, "increment past end of StrBlobPtr");

 ++curr; // инкремент текущего состояния

 return *this;

}

Безусловно, чтобы получить доступ к переменной-члену data, наш класс указателя должен быть дружественным классу StrBlob (см. раздел 7.3.4). Снабдим также класс StrBlob функциями begin() и end(), возвращающими указатель StrBlobPtr на себя:

// предварительное объявление необходимо для объявления дружественным

// классу StrBlob

class StrBlobPtr;

class StrBlob {

 friend class StrBlobPtr;

 // другие члены, как в разделе 12.1.1

 // возвратить указатель StrBlobPtr на первый и следующий

 // после последнего элементы

 StrBlobPtr begin() { return StrBlobPtr(*this); }

 StrBlobPtr end()

  { auto ret = StrBlobPtr(*this, data->size());

    return ret; }

};

Упражнения раздела 12.1.6

Упражнение 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.

12.2. Динамические массивы

Операторы new и delete резервируют объекты по одному. Некоторым приложениям нужен способ резервировать хранилище для многих объектов сразу. Например, векторы и строки хранят свои элементы в непрерывной памяти и должны резервировать несколько элементов сразу всякий раз, когда контейнеру нужно повторное резервирование (см. раздел 9.4).

Для этого язык и библиотека предоставляют два способа резервирования всего массива объектов. Язык определяет второй вид оператора new, резервирующего и инициализирующего массив объектов. Библиотека предоставляет шаблон класса allocator, позволяющий отделять резервирование от инициализации. По причинам, описанным в разделе 12.2.2, применение класса allocator обычно обеспечивает лучшую производительность и более гибкое управление памятью.

1 ... 157 158 159 160 161 162 163 164 165 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии