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

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

Наш ресурс дает возможность бесплатно читать книгу онлайн Стенли Липпман - Язык программирования C++. Пятое издание. Жанр: Программирование издательство -, год -. Сайт books-read.com дает возможность читать полную версию книги без регистрации и sms. Все книги онлайн, не надо качать fb2, epub, txt.
Добавить книгу Стенли Липпман - Язык программирования C++. Пятое издание в приложение ЧИТАТЬ КНИГУ ОФЛАЙН в приложении ios/android
Перейти на страницу:

Операторы инкремента и декремента работают подобным образом — они вызывают функцию check() для проверки допустимости объекта класса StrBlobPtr. Если это так, то функция check() проверяет также допустимость данного индекса. Если функция check() не передает исключения, эти операторы возвращают ссылку на свой объект.

В случае инкремента функции check() передается текущее значение curr. Пока это значение меньше размера основного вектора, функция check() завершается нормально. Если значение curr находится за концом вектора, функция check() передает исключение:

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

// или декремента

StrBlobPtr& StrBlobPtr::operator++() {

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

 // невозможен

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

 ++curr; // переместить текущую позицию вперед

 return *this;

}

StrBlobPtr& StrBlobPtr::operator--() {

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

 --curr; // переместить текущую позицию назад

 check(-1, "decrement past begin of StrBlobPtr");

 return *this;

}

Оператор декремента уменьшает значение curr прежде, чем вызвать функцию check(). Таким образом, если значение curr (беззнаковое) уже является нулем, передаваемое функции check() значение будет наибольшим позитивным значением, представляющим недопустимый индекс (см. раздел 2.1.2).

Дифференциация префиксных и постфиксных операторов

При определении префиксных и постфиксных операторов возникает одна проблема: каждый из них имеет одинаковое имя и получает одинаковое количество параметров того же типа. При обычной перегрузке невозможно отличить префиксную и постфиксную версии оператора.

Для решения этой проблемы постфиксные версии получают дополнительный (неиспользуемый) параметр типа int. При использовании постфиксного оператора компилятор присваивает этому параметру аргумент 0. Хотя постфиксная функция вполне может использовать этот дополнительный параметр, как правило, так не поступают. Этот параметр не нужен для работы, обычно выполняемой постфиксным оператором. Его основная задача заключается в том, чтобы отличить определение постфиксной версии функции от префиксной.

Теперь в класс CheckedPtr можно добавить постфиксные операторы:

class StrBlobPtr {

public:

 // инкремент и декремент

 StrBlobPtr operator++(int); // постфиксные операторы

 StrBlobPtr operator--(int);

 // другие члены как прежде

};

Для совместимости со встроенными операторами постфиксные операторы должны возвращать прежнее значение (существовавшее до декремента или инкремента). Оно должно быть возвращено как значение, а не как ссылка.

Постфиксные версии должны запоминать текущее состояние объекта прежде, чем изменять объект:

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

// неизмененное значение

StrBlobPtr StrBlobPtr::operator++(int) {

 // здесь проверка не нужна, ее выполнит префиксный инкремент

 StrBlobPtr ret = *this; // сохранить текущее значение

 ++*this;                // на один элемент вперед, проверку

                         // осуществляет оператор инкремента

 return ret;             // возврат сохраненного значения

}

StrBlobPtr StrBlobPtr::operator--(int) {

 // здесь проверка не нужна, ее выполнит префиксный декремент

 StrBlobPtr ret = *this; // сохранить текущее значение

 --*this;                // на один элемент назад, проверку

                         // осуществляет оператор декремента

 return ret;             // возврат сохраненного значения

}

Для выполнения фактического действия каждый из этих операторов вызывает собственную префиксную версию. Например, постфиксный оператор инкремента использует такой вызов префиксного оператора инкремента:

++*this

Этот оператор проверяет безопасность приращения и либо передает исключение, либо осуществляет приращение значения curr. Если функция check() не передает исключения, постфиксные функции завершают работу, возвращая сохраненные ранее копии значений. Таким образом, после выхода сам объект будет изменен, но возвращено будет первоначальное, не измененное значение.

Поскольку параметр типа int не используется, имя ему присваивать не нужно.

Явный вызов постфиксных операторов

Как упоминалось в разделе 14.1, в качестве альтернативы использованию перегруженного оператора в выражении можно вызвать его явно. Если постфиксная версия задействуется при помощи вызова функции, то следует передать значение и для целочисленного аргумента:

StrBlobPtr p(a1); // p указывает на вектор в a1

p.operator++(0);  // вызов постфиксного оператора operator++

p.operator++();   // вызов префиксного оператора operator++

Переданное значение обычно игнорируется, но оно позволяет предупредить компилятор о том, что требуется именно постфиксная версия оператора.

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

Упражнение 14.27. Добавьте в класс StrBlobPtr операторы инкремента и декремента.

Упражнение 14.28. Определите для класса StrBlobPtr операторы сложения и вычитания, чтобы они реализовали арифметические действия с указателями (см. раздел 3.5.3).

Упражнение 14.29. Почему не были определены константные версии операторов инкремента и декремента?

14.7. Операторы доступа к членам

Операторы обращения к значению (*) и стрелка (->) обычно используются в классах, представляющих итераторы, и в классах интеллектуального указателя (см. раздел 12.1). Вполне логично добавить эти операторы в класс StrBlobPtr:

class StrBlobPtr {

public:

 std::string& operator*() const {

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

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

                     // объект

 }

 std::string* operator->() const {

  // передать реальную работу оператору обращения к значению

  return &this->operator*();

 }

 // другие члены как прежде

};

Оператор обращения к значению проверяет принадлежность curr диапазону, и если это так, то возвращает ссылку на элемент, обозначенный значением curr. Оператор стрелки не делает ничего сам, он вызывает оператор обращения к значению и возвращает адрес возвращенного им элемента.

Оператор стрелка (arrow) должен быть определен как функция-член класса. Оператор обращения к значению (dereference) необязательно должен быть членом класса, но, как правило, его тоже определяют как функцию-член.

Следует заметить, что эти операторы определены как константные члены. В отличие от операторов инкремента и декремента, выборка элемента никак не изменяет состояния объекта класса StrBlobPtr. Обратите также внимание на то, что эти операторы возвращают ссылку или указатель на неконстантную строку. Причина этого в том, что объект класса StrBlobPtr, как известно, может быть связан только с неконстантным объектом класса StrBlob (см. раздел 12.1.6).

Эти операторы можно использовать таким же способом, которым используются соответствующие операторы с указателями и итераторами вектора:

StrBlob a1 = {"hi", "bye", "now"};

StrBlobPtr p(a1);            // p указывает на вектор в a1

*p = "okay";                 // присвоить первый элемент a1

cout << p->size() << endl;   // выводит 4, размер первого элемента в a1

cout << (*p).size() << endl; // эквивалент p->size()

Ограничения на возвращаемое значение оператора стрелки

Подобно большинству других операторов (хотя это и плохая идея), оператор operator* можно определить как выполняющий некие действия по своему усмотрению. Таким образом, оператор operator* можно определить как возвращающий, например, фиксированное значение, скажем, 42, или выводящий содержимое объекта, к которому он применен, или что то еще. Но для перегруженного оператора стрелки это не так. Оператор стрелки никогда не изменяет своего фундаментального назначения: доступа к члену класса. При перегрузке оператора стрелки можно изменить объект, из которого стрелка выбирает определенный член, но нельзя изменить тот факт, что она выбирает член класса.

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

Стенли Липпман читать все книги автора по порядку

Стенли Липпман - на сайте онлайн книг books-read.com Вы можете читать полные версии книг автора в одном месте.


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

Отзывы читателей о книге Язык программирования C++. Пятое издание, автор: Стенли Липпман. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор books-read.com


Прокомментировать
Подтвердите что вы не робот:*