Эффективное использование 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[].