Стандарты программирования на С++. 101 правило и рекомендация
Шрифт:
Иногда классы обязаны предоставить доступ ко внутренним дескрипторам по причинам, связанным с совместимостью, например, для интерфейса со старым кодом или при использовании других систем. Например,
[С++03] §23 • [Dewhurst03] §80 • [Meyers97] #29 • [Saks99] • [Stroustrup00] §7.3 • [Sutter02] §9
43. Разумно пользуйтесь идиомой Pimpl
С++ делает закрытые члены недоступными, но не невидимыми. Там, где это оправдывается получаемыми преимуществами, следует подумать об истинной невидимости, достигаемой применением идиомы Pimpl (указателя на реализацию) для реализации брандмауэров компилятора и повышения сокрытия информации (см. рекомендации 11 и 41).
Когда имеет смысл создать "брандмауэр компилятора", который полностью изолирует вызывающий код от закрытых частей класса, воспользуйтесь идиомой Pimpl (указателя на реализацию): скройте их за непрозрачным указателем (указатель (предпочтительно подходящий интеллектуальный) на объявленный, но пока не определенный класс). Например:
Дающий название идиоме указатель должен использоваться для хранения всех закрытых членов, как данных, так и закрытых функций-членов. Это позволяет вам вносить произвольные изменения в закрытые детали реализации ваших классов без какой бы то ни было рекомпиляции вызывающего кода. Свобода и независимость — вот отличительные черты рассматриваемой идиомы (см. рекомендацию 41).
Примечание: объявляйте указатель на закрытую реализацию, как показано — с использованием двух объявлений. Если вы скомбинируете две строки с предварительным объявлением типа и указателя на него в одну инструкцию
Имеется как минимум три причины для использования Pimpl, и все они вытекают из различия между доступностью (в состоянии ли вы вызвать или использовать некоторый объект) и видимостью (видим ли этот объект для вас и, таким образом, зависите ли вы от его определения) в С++. В частности, все закрытые члены класса недоступны никому, кроме функций- членов и друзей, но зато видимы всем — любому коду, которому видимо определение класса.
Первое следствие этого — потенциально большее время сборки приложения из-за обработки излишних определений типов. Для закрытых данных-членов, хранящихся по значению, и параметров закрытых функций-членов, передаваемых по значению или используемых в видимой реализации функций, типы должны быть определены, даже если они никогда не потребуются в данной единице компиляции. Это может привести к увеличению времени сборки, например:
Заголовочный файл, содержащий определение класса С, должен также включать заголовочный файл, содержащий определение
Второе следствие — создание неоднозначностей и сокрытие имен для кода, который пытается вызвать функцию. Несмотря на то, что закрытая функция-член не может быть вызвана кодом вне ее класса и его друзей, она тем не менее участвует в поиске имен и разрешении перегрузки и тем самым может сделать вызов неоднозначным или некорректным. Перед выполнением проверки доступности С++ выполняет поиск имен и разрешение перегрузки. Из-за этого видимость имеет более высокий приоритет:
В строке А обходной путь состоит в том, чтобы явно квалифицировать вызов как
Третье следствие влияет на обработку ошибок и безопасность. Рассмотрим пример
Коротко говоря, мы не можем написать оператор