Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 26
>class TextBlock {
>public:
>...
>const char& operator[](std::size_t position) const
>{
>... // выполнить проверку границ массива
>... // протоколировать доступ к данным
>... // проверить целостность данных
>return text[position];
>}
>char& operator[](std::size_t position) const
>{
>... // выполнить проверку границ массива
>... // протоколировать доступ к данным
>... // проверить целостность данных
>return text[position];
>}
>private:
>std:string text;
>};
Ох! Налицо все неприятности, связанные с дублированием кода: увеличение времени компиляции, размера программы и неудобство сопровождения. Конечно, можно переместить весь код для проверки выхода за границы массива и прочего в отдельную функцию-член (естественно, закрытую), которую будут вызывать обе версии operator[], но обращения к этой функции все же будут дублироваться.
В действительности было бы желательно реализовать функциональность operator[] один раз, а использовать в двух местах. То есть одна версия operator[] должна вызывать другую. И это подводит нас к вопросу об отбрасывании константности.
С самого начала отметим, отбрасывать константность нехорошо. Я посвятил целое правило 27 тому, чтобы убедить вас не делать этого, но дублирование кода – тоже не сахар. В данном случае константная версия operator[] делает в точности то же самое, что неконстантная, и отличие между ними – лишь в присутствии модификатора const. В этой ситуации отбрасывать const безопасно, поскольку пользователь, вызывающий неконстантный operator[], так или иначе должен получить неконстантный объект. Ведь в противном случае он не стал бы вызывать неконстантную функцию. Поэтому реализация неконстантного operator[] путем вызова константной версии – это безопасный способ избежать дублирования кода, даже пусть даже для этого требуется воспользоваться оператором const_cast. Ниже приведен получающийся в результате код, но он станет яснее после того, как вы прочитаете следующие далее объяснения:
>class TextBlock {
>public:
>...
>const char& operator[](std::size_t position) const // то же, что и раньше
>{
>...
>...
>...
>return text[position];
>}
>char& operator[](std::size_t position) const // теперь просто
>// вызываем const op[]
>{
>return
>const_cast
>// op[] исключить const
>static_cast
>// *this
>[position] // вызвать константную
>); // версию op[]
>}
>...
>};
Как видите, код включает два приведения, а не одно. Мы хотим, чтобы неконстантный operator[] вызывал константный, но если внутри неконстантного оператора [] просто вызовем operator[], то получится рекурсивный вызов. Во избежание бесконечной рекурсии нужно указать, что мы хотим вызвать const operator[], но прямого способа сделать это не существует. Поэтому мы приводим *this от типа TextBlock& к const TextBlock&. Да, мы выполняем приведение, чтобы