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>

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

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

 std::weak_ptr> 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>

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 на себя:

Перейти на страницу:

Похожие книги