Стандарты программирования на С++. 101 правило и рекомендация | страница 75
• Класс не может изменять внутреннюю реализацию своей абстракции, поскольку от нее зависят клиенты. Если в будущем класс >Socket
будет обновлен для поддержки другого протокола с использованием других низкоуровневых примитивов, вызывающий код, который будет по-прежнему получать доступ к дескриптору >handle_
и работать с ним, окажется некорректным.
• Класс не в состоянии обеспечить выполнение его инвариантов, поскольку вызывающий код может изменить состояние без ведома класса. Например, кто-то может закрыть дескриптор, используемый объектом >Socket
, минуя вызов функции-члена Socket, а это приведет к тому, что объект станет недействительным.
• Код клиента может хранить дескрипторы, возвращаемые вашим классом, и пытаться использовать их после того, как код вашего класса сделает их недействительными.
Распространенная ошибка заключается в том, что действие const на самом деле неглубокое и не распространяется посредством указателей (см. рекомендацию 15). Например, >Socket::GetHandle
— константный член; пока мы рассматриваем ситуацию с точки зрения компилятора, возврат >handlе_
сохраняет константность объекта. Однако непосредственный вызов функций операционной системы с использованием значения >handlе_
вполне может изменять данные, к которым косвенно обращается >handlе_
.
Приведенный далее пример очень прост, хотя в данном случае ситуация несколько лучше — мы можем снизить вероятность случайного неверного употребления возвращаемого значения, описав его тип как >const
:
>class String {
> char* buffer_;
>public:
> char* GetBuffer() const { return buffer_; }
> // Плохо: следует возвращать const char*
> // ...
>};
Хотя функция >GetBuffer
константная, технически этот код вполне корректен. Понятно, что клиент может использовать эту функцию >GetBuffer
для того, чтобы изменить объект >String
множеством разных способов, не прибегая к явному преобразованию типов. Например, >strcpy(s.GetBuffer(), "Very Long String...")
— вполне законный код; любой компилятор пропустит его без каких бы то ни было замечаний. Если бы мы объявили возвращаемый тип как >const char*
, то представленный код вызвал бы, по крайней мере, ошибку времени компиляции, так что случайно поступить столь опасно было бы просто невозможно — вызывающий код должен был бы использовать явное преобразование типов (см. рекомендации 92 и 95).
Но даже возврат указателей на >const
не устраняет возможности случайного некорректного использования, поскольку имеется еще одна проблема, связанная с корректностью внутренних данных класса. В приведенном выше примере с классом