членами является вполне разумным, поэтому C++ имеет механизм его поддержки: ключевое слово mutable.

Для того чтобы curIndex_ можно было обновлять в константной функции-члене, объявите ее с ключевым словом mutable в объявлении класса.

mutable int curIndex_;

Это позволит вам модифицировать curIndex_ в любом месте. Однако этой возможностью следует пользоваться разумно, поскольку это действует на вашу функцию так, как будто она становится с этого момента неконстантной.

Применение ключевого слова const в примере 15.4 позволяет гарантировать невозможность изменения состояния объекта в функции-члене. В целом, такой подход дает хорошие результаты, потому что сообщает пользователям класса о режиме работы функции-члена и потому что сохраняет вам репутацию, заставляя компилятор проконтролировать отсутствие в функции-члене непредусмотренных действий.

15.5. Написание оператора, не являющегося функцией-членом

Проблема

Необходимо написать бинарный оператор, и вы не можете или не хотите сделать его функцией- членом класса.

Решение

Используйте ключевое слово operator, временную переменную и конструктор копирования для выполнения основной работы и возвратите временный объект. В примере 15.5 приводится простой оператор конкатенации строк для пользовательского класса String.

Пример 15.5. Конкатенация с использованием оператора не члена

#include <iostream>

#include <cstring>

class String { // Предположим, что объявление класса String содержит,

               // по крайней мере, все, что указанно ниже

public:

 String();

 String(const char* p);

 String(const String& orig);

 ~String() {delete buf_;}

 String& append(const String& s);

 size_t length() const;

 const char* data() const;

 String& operator=(const String& orig);

 // ...

};

String operator+(const String& lhs, const String& rhs) {

 String tmp(lhs); // Сконструировать временный объект с помощью

                  // конструктора копирования

 tmp.append(rhs); // Использовать функцию-член для выполнения реальной

                  // работы

 return(tmp); // Возвратить временный объект

}

int main() {

 String s1('banana ');

 String s2('rancher');

 String s3, s4, s5, s6;

 s3 = s1 + s2;           // Работает хорошо, но с сюрпризами

 s4 = s1 + 'rama';       // Автоматически конструируется 'rama', используя

                         // конструктор String(const char*)

 s5 = 'ham ' + s2;       // Круто, то же самое можно делать даже

 s6 = s1 + 'rama ' + s2; // с другим операндом

 std::cout << 's3 = ' << s3.data() << ' ';

 std::cout << 's4 = ' << s4.data() << ' ';

 std::cout << 's5 = ' << s5.data() << ' ';

 std::cout << 's6 = ' << s6.data() << ' ';

}

Обсуждение

Независимый оператор объявляется и определяется подобно оператору функции-члена. В примере 15.5 я мог бы реализовать operator+ как функцию-член, объявляя ее следующим образом.

String operator+(const String& rhs);

В большинстве случаев это будет работать одинаково, независимо от того, определяется ли operator+ как функция-член или нет, однако существует, по крайней мере, две причины, по которым желательно реализовать его не как функцию-член. Первая причина концептуальная, имеет ли смысл иметь оператор, который возвращает новый, отличный от других объект? operator+, реализованный как функция-член, не проверяет и не изменяет состояние объекта. Это служебная функция общего назначения, которая в данном случае работает со строками типа String и, следовательно, не должна являться функцией членом.

Вторая причина техническая. При использовании оператора-члена вы не сможете выполнить следующую операцию (из приведенного выше примера).

s5 = 'ham ' + s2;

Это не сработает, потому что символьная строка не имеет operator+, который принимает String в качестве параметра. С другой стороны, если вы определили независимый operator+, который принимает два параметра типа String, ваш компилятор проверит наличие в классе String конструктора, принимающего const char* в качестве аргумента (или любой другой тип, который вы используете совместно с String), и сконструирует временный объект на этапе выполнения. Поэтому приведенная выше строка эквивалентна следующей.

s5 = String('ham ') + s2;

Компилятор позволяет вам немного сэкономить ваши действия и не вводить несколько символов за счет поиска и вызова соответствующего конструктора.

Перегрузка операторов сдвига потоков влево и вправо (<< и >>) также требует применения операторов не-членов. Например, для записи нового объекта в поток, используя сдвиг влево, вам придется следующим образом объявить operator<<:

ostream& operator<<(ostream& str, const MyClass& obj);

Конечно, вы можете создать подкласс одного из классов потока стандартной библиотеки и добавить все необходимые вам операторы сдвига влево, но будет ли такое решение действительно удачным? При таком решении только тот программный код, который использует ваш новый класс потока, сможет записывать в него объекты вашего специального класса. Если вы используете независимый оператор,

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату
×