Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
Шрифт:
Впрочем, функции, которые возвращают указатели на функции-члены, встречаются нечасто, поэтому вернемся к классу Rectangle и его функциям-членам upperLeft и lowerRight. Обе проблемы, которые мы идентифицировали для этих функций, могут быть исключены простым применением квалификатора const к их возвращаемому типу:
В
Но даже и так upperLeft и lowerRight по-прежнему возвращают «дескрипторы» внутренних данных объекта, и это может вызвать проблемы иного свойства. В частности, возможно появление «висячих дескрипторов» (dangling handles), то есть дескрипторов, ссылающихся на части уже не существующих объектов. Наиболее типичный источник таких исчезнувших объектов – значения, возвращаемые функциями. Например, рассмотрим функцию, которая возвращает ограничивающий прямоугольник объекта GUI:
Теперь посмотрим, как пользователь может применить эту функцию:
Вызов boundingBox вернет новый временный объект Rectangle. Этот объект не имеет имени, поэтому назовем его temp. Затем вызывается функция-член upperLeft объекта temp, и этот вызов возвращает ссылку на внутренние данные temp, в данном случае на один из объектов Point. В результате pUpperLeft указывает на этот объект Point. До сих пор все шло хорошо, но мы еще не закончили, поскольку в конце предложения возвращенное boundingBox значение – temp – будет разрушено, а это приведет к разрушению объектов Point, принадлежавших temp. То есть pUpperLeft теперь указывает на объект, который более не существует. Указатель PUpperLeft становится «висячим» уже в конце предложения, где он создан!
Вот почему опасна любая функция, которая возвращает «дескриптор» внутренних данных объекта. При этом не важно, является ли «дескриптор» ссылкой, указателем или итератором. Не важно, что она квалифицирована const. Не важно, что сама функция-член, возвращающая «дескриптор», является константной. Имеет значение лишь тот факт, что «дескриптор» возвращен, поскольку возникает опасность, что он «переживет»
объект, с которым связан.Это не значит, что никогда не следует писать функции-члены, возвращающие дескрипторы. Иногда это бывает необходимо. Например, operator[] позволяет вам обращаться к отдельному элементу строки или вектора, и работает он, возвращая ссылку на данные в контейнере (см. правило 3), которые уничтожаются вместе с контейнером. Но все же такие функции – скорее исключение, чем правило.
• Избегайте возвращать «дескрипторы» (ссылки, указатели, итераторы) внутренних данных объекта. Это повышает степень инкапсуляции, помогает константным функциям-членам быть константными и минимизирует вероятность появления «висячих дескрипторов».
Правило 29: Стремитесь, чтобы программа была безопасна относительно исключений
Безопасность исключений в чем-то подобна беременности… но пока отложим эту мысль в сторонку. Нельзя всерьез говорить о репродуктивной функции, пока не завершился этап ухаживания.
Предположим, что у нас есть класс, представляющий меню с фоновыми картинками в графическом интерфейсе пользователя. Этот класс предназначен для использования в многопоточной среде, поэтому он включает мьютекс для синхронизации доступа:
Рассмотрим следующую возможную реализацию функции-члена change-Background:
С точки зрения безопасности исключений, эта функция настолько плоха, насколько вообще возможно. К безопасности исключений предъявляется два требования, и она не удовлетворяет ни одному из них.
Когда возбуждается исключение, то безопасная относительно исключений функция:
• Не допускает утечки ресурсов. Приведенный код не проходит этот тест, потому что если выражение «new Image(imgSrc)» возбудит исключение, то вызов unlock никогда не выполнится, и мьютекс окажется захваченным навсегда.
• Не допускает повреждения структур данных. Если «new Image(imgSrc)» возбудит исключение, в bgImage останется указатель на удаленный объект. Кроме того, счетчик imageChanges увеличивается, несмотря на то что новая картинка не установлена. (С другой стороны, старая картинка уже полностью удалена, так что трудно сделать вид, будто ничего не изменилось.)