Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 23
Константные функции-члены
Назначение модификатора const в объявлении функций-членов – определить, какие из них можно вызывать для константных объектов. Такие функции-члены важны по двум причинам. Во-первых, они облегчают понимание интерфейса класса, ведь полезно сразу видеть, какие функции могут модифицировать объект, а какие нет. Во-вторых, они обеспечивают возможность работать с константными объектами. Это очень важно для написания эффективного кода, потому что, как объясняется в правиле 20, один из основных способов повысить производительность программ на C++ – передавать объекты по ссылке на константу. Но эта техника будет работать только в случае, когда функции-члены для манипулирования константными объектами объявлены с модификатором const.
Многие упускают из виду, что функции, отличающиеся только наличием const в объявлении, могут быть перегружены. Это, однако, важное свойство C++. Рассмотрим класс, представляющий блок текста:
>class TextBlock {
>public:
>...
>const char& operator[](std::size_t position) const // operator[] для
>{return text[position];} // константных объектов
>char& operator[](std::size_t position) // operator[] для
>{return text[position];} // неконстантных объектов
>private:
>std::string text;
>};
Функцию operator[] в классе TextBlock можно использовать следующим образом:
>TextBlock tb(“Hello”);
>Std::cout << tb[0]; // вызов неконстантного
>// оператора TextBlock::operator[]
>const TextBlock ctb(“World”);
>Std::cout << ctb[0]; // вызов константного
>// оператора TextBlock::operator[]
Кстати, константные объекты чаще всего встречаются в реальных программах в результате передачи по указателю или ссылке на константу. Приведенный выше пример ctb является довольно искусственным. Но вот вам более реалистичный:
>void print(const TextBlock& ctb) // в этой функции ctb – ссылка
>// на константный объект
>{
>std::cout << ctb[0]; // вызов const TextBlock::operator[]
>...
>}
Перегружая operator[] и создавая различные версии с разными возвращаемыми типами, вы можете по-разному обрабатывать константные и неконстантные объекты TextBlock:
>std::cout << tb[0]; // нормально – читается
>// неконстантный TextBlock
>tb[0] = ‘x’; // нормально – пишется
>// неконстантный TextBlock
>std::cout << ctb[0]; // нормально – читается
>// константный TextBlock
>ctb[0] = ‘x’; // ошибка! – запись
>// константного TextBlock
Отметим, что ошибка здесь связана только с типом значения, возвращаемого operator[]; сам вызов operator[] проходит нормально. Причина ошибки – в попытке присвоить значение объекту типа const char&, потому что это именно такой тип возвращается константной версией operator[].