Стандарты программирования на С++. 101 правило и рекомендация | страница 77



Примечание: объявляйте указатель на закрытую реализацию, как показано — с использованием двух объявлений. Если вы скомбинируете две строки с предварительным объявлением типа и указателя на него в одну инструкцию >struct Impl *pimpl; это будет вполне законно, но изменит смысл объявления: в этом случае >Impl находится в охватывающем пространстве имен и не является вложенным типом вашего класса.

Имеется как минимум три причины для использования Pimpl, и все они вытекают из различия между доступностью (в состоянии ли вы вызвать или использовать некоторый объект) и видимостью (видим ли этот объект для вас и, таким образом, зависите ли вы от его определения) в С++. В частности, все закрытые члены класса недоступны никому, кроме функций- членов и друзей, но зато видимы всем — любому коду, которому видимо определение класса.

Первое следствие этого — потенциально большее время сборки приложения из-за обработки излишних определений типов. Для закрытых данных-членов, хранящихся по значению, и параметров закрытых функций-членов, передаваемых по значению или используемых в видимой реализации функций, типы должны быть определены, даже если они никогда не потребуются в данной единице компиляции. Это может привести к увеличению времени сборки, например:

>class C {

> // ...

>private:

> AComplicatedType act_;

>}

Заголовочный файл, содержащий определение класса С, должен также включать заголовочный файл, содержащий определение >AComplicatedType, который в свою очередь транзитивно включает все заголовочные файлы, которые могут потребоваться для определения >AComplicatedType, и т.д. Если заголовочные файлы имеют большие размеры, время компиляции может существенно увеличиться.

Второе следствие — создание неоднозначностей и сокрытие имен для кода, который пытается вызвать функцию. Несмотря на то, что закрытая функция-член не может быть вызвана кодом вне ее класса и его друзей, она тем не менее участвует в поиске имен и разрешении перегрузки и тем самым может сделать вызов неоднозначным или некорректным. Перед выполнением проверки доступности С++ выполняет поиск имен и разрешение перегрузки. Из-за этого видимость имеет более высокий приоритет:

>int Twice(int);        // 1


>class Calc {

>public:

> string Twice(string); // 2


>private:

> char* Twice(char*);   // 3


> int Test() {

>  return Twice(21);   // A: ошибка, функции 2 и 3 не

>   // подходят (могла бы подойти функция 1, но

>   // ее нельзя рассматривать, так она скрыта от