Стандарты программирования на С++. 101 правило и рекомендация | страница 43
Предпочитайте использовать предварительные объявления везде, где не требуется полное определение типа. Полное определение класса С требуется в двух основных случаях.
• Когда вам необходимо знать размер объекта С. Например, при создании объекта С в стеке или при использовании его в качестве непосредственно хранимого (не через указатель) члена другого класса.
• Когда вам требуется имя или вызов члена С. Например, когда вы вызываете функцию-член этого класса.
Не будем рассматривать в этой книге тривиальные случаи циклических зависимостей, которые приводят к ошибкам компиляции — думаем, вы успешно справитесь с ними, воспользовавшись многими хорошими советами, имеющимися в литературе и рекомендации 1. Мы же обратимся к циклическим зависимостям, которые остаются в компилируемом коде, и посмотрим, как они влияют на его качество и какие действия следует предпринять, чтобы избежать их.
В общем случае зависимости и их циклы следует рассматривать на уровне модулей. Модуль представляет собой образующий единое целое набор совместно опубликованных классов и функций (см. рекомендацию 5). Циклическая зависимость в простейшем виде представляет собой два класса, непосредственно зависящих друг от друга
>class Child; // Устранение циклической зависимости
>class Parent { // ...
> Child* myChild_;
>};
>// возможно, в другом заголовочном файле
>class Child { // ...
> Parent* myParent_;
>};
Классы >Parent
и >Child
зависят друг от друга. Приведенный код компилируется, но мы сталкиваемся с фундаментальной проблемой: эти два класса больше не являются независимыми, и более того, становятся взаимозависимы друг от друга. Это не всегда плохо, но должно иметь место лишь в том случае, когда оба класса являются частями одного и того же модуля (разработанного одним и тем же человеком или командой, оттестированного и выпущенного как единое целое).
В противоположность описанной ситуации, рассмотрим, что будет, если класс >Child
не должен будет хранить обратную связь с объектом >Parent
? Тогда >Child
может быть выпущен в собственном отдельном небольшом модуле (и, возможно, под другим именем), полностью независимо от >Parent
— что, конечно, существенно повышает гибкость проектирования.
Все становится еще хуже, когда зависимости охватывают несколько модулей. Такой мощный "клей", как зависимости, объединяют эти модуля в единую монолитную публикуемую единицу. Вот почему циклы являются самыми страшными врагами модульности.