Разрешение циклических зависимостей с использованием предварительных объявлений - PullRequest
2 голосов
/ 04 мая 2019

Ради этого вопроса, пожалуйста, не обращайте внимания на проблемы разработки программного обеспечения, связанные с взаимными зависимостями, и сосредоточьтесь на техническом аспекте.

Пожалуйста, рассмотрите два заголовочных файла, a.h и b.h.Файл a.h определяет некоторую структуру, которая требуется b.h.Тем не менее, b.h также определяет структуру, которая необходима для a.h.

Насколько я понимаю, обычный метод разрешения этой циклической зависимости #include заключается в b.h вместо включения a.h,он бы заранее объявил определение структуры, которое требуется от a.h.Это будет выглядеть примерно так:

ах

// No nice typedef
struct MyThing_t {
    // ...
};

Bh

// Other things omitted 

typedef struct MyThing_t MyThing;
// ... proceed using MyThing

Как я понимаю,это должно компилироваться и работать.

Но - почему это работает?Как компилятор узнает имя MyThing_t при компиляции b.h?Какой здесь механизм?

Также - почему бы не включить typedef также в a.h, для использования с использованием модулей, с которыми a.h не имеет круговой зависимости?

Ответы [ 3 ]

2 голосов
/ 04 мая 2019

Самый простой способ справиться с этой ситуацией - определить структуру в другом заголовке, таком как basic.h, или в некотором другом, и оба a.h и b.h включают этот заголовок. Потому что в b.h это

typedef struct MyThing_t MyThing

В прямом объявлении

объявляется тип непрозрачный , который позволяет использовать только указатель MyThing*. Потому что b.h не имеет представления о том, каков размер или структура памяти структуры на самом деле. На самом деле это очень распространенная идиома языка C, так как она позволяет инкапсулировать внутренние компоненты структуры и разрешает объявление в заголовочном файле функций, которые работают с этим указателем, без необходимости знать реализацию.

0 голосов
/ 04 мая 2019

Насколько я понимаю, это должно скомпилироваться и работать.

Да и нет. Каждый заголовок может быть #include d в исходном файле независимо от другого, но B.h не дает определения struct MyThing_t. Там, где нет определения, это неполный тип, который сильно ограничивает то, что вы можете с ним делать.

Но - почему это работает? Как компилятор знает имя MyThing_t при компиляции b.h?

Если он еще не знает о struct MyThing_t до того, как достигнет typedef, typedef делает осведомленным.

Какой здесь механизм?

Тип структуры с тегом может использоваться без предварительного объявления или определения этого типа, но только в тех случаях, когда может использоваться неполный тип. Это то, что позволяет структурам содержать указатели на объекты их собственного типа. Он может использоваться более широко для объявления указателей на рассматриваемый тип, а также может использоваться в typedef s. С другой стороны, неполные типы нельзя использовать любым способом, который требует знания их размера хранилища или (для типов структуры и объединения) имен или типов их членов.

Также - почему я не должен включать typedef также в a.h, для использования использование модулей, с которыми у a.h нет циклической зависимости?

Не знаю, а почему бы и нет? Это было бы более типично, чем разделять их. Вы должны быть осторожны с тем, чтобы объявить один и тот же typedef дважды. Это допустимо в последних версиях C, но не разрешено в C90 и C99.

0 голосов
/ 04 мая 2019

Это typedef *struct* MyThing_t MyThing;; для struct Mything_t объявление не требуется (что может привести к ошибкам, поэтому хорошая идея для typedefs-for-everything).

Я считаю, что лучше написать один fwd.h файл (или для больших проектов для каждого каталога) со всеми определениями типов в нем и включить его в начало всех других заголовков.

Кстати, вам следует избегать имен, заканчивающихся на _t, если вы не работаете в POSIX.

...