Алгоритмы, структуры данных
5c8b6e8c

С++


Язык С++ позволяет пометить метод как константный [1]; неконстантные методы этого объекта запрещается использовать в теле помеченного метода, и в контексте этого метода ссылки на сам объект и все его поля константны. Также существует возможность пометить ссылку (указатель) как константную. Применительно к ссылке свойство константности означает, что через эту ссылку можно вызывать только константные методы; присвоение константной ссылки неконстантной запрещено. Проверку этих условий обеспечивает компилятор. Для обозначения константности используется модификатор const. Пример интерфейса с константными методами:

В примере методы Name, Age, Picture объявлены константными. Кроме того, можно наблюдать и использование константных ссылок: параметр методов SetName и SetPicture, возвращаемое значение методов Name и Picture. Компилятор обеспечит проверку того, что реализация константных методов не имеет побочных эффектов в виде изменения состояния объекта, реализующего интерфейс Personal. Как только обнаружится попытка выполнить запрещенную операцию, компилятор сообщит об ошибке.

Предположим, мы намерены оптимизировать работу метода Picture. Вовсе не обязательно, что во всех вариантах использования класса Personal будет требоваться получить фотографию, занимающую много памяти. Можно так реализовать метод Picture, чтобы фотография загружалась только в случае использования именно этого метода. Но в Picture нельзя менять переменную picture_data, которая хранит ссылку на фотографию: компилятор запрещает менять поля объекта в константном методе. Это и есть проблема логической неизменности. Пожалуй, самым распространенным случаем возникновения несоответствия логической и физической неизменности является кэширование данных и создание прокси-объектов [9]. Таким образом, в С++ модификатор const означает лишь физическую неизменность. Чтобы обеспечить возможность реализации логически константных методов, в С++ имеется конструкция const_cast. С ее помощью приведенный пример можно переписать так:




Итак, проблема логической неизменности решена; метод Picture не меняет наблюдаемого состояния объекта. Однако одновременно потеряна защита со стороны компилятора, и теперь модификатор const не гарантирует, что состояние объекта остается неизменным.

Для ослабления контроля неизменности со стороны компилятора в С++ также используется модификатор mutable. Им можно пометить поля класса, значения которых разрешается менять в константных методах. Посмотрим, как будет выглядеть пример с использованием mutable (приведены только те элементы, которые изменены по сравнению с вариантом для const_cast).



Увы, этот вариант имеет тот же недостаток, что и вариант с const_cast. Проверка со стороны компилятора ослабляется, не предлагая взамен никаких гарантий. Если, в силу ошибочной реализации, при каждом вызове метода Picture поле picture_data модифицируется, то это не будет обнаружено, а клиент станет каждый раз получать разные изображения одной и той же персоны.

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


Содержание раздела