C циклы заголовочных файлов

у меня есть пара заголовочных файлов, которые сводятся к:

дерево.h:

#include "element.h"

typedef struct tree_
{
    struct *tree_ first_child;
    struct *tree_ next_sibling;
    int tag;
    element *obj;
    ....
} tree;

и элемент.h:

#include "tree.h"

typedef struct element_
{
    tree *tree_parent;
    char *name;
    ...
} element;

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

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

Как разрешить эти типы циклов (я думаю, что это может иметь какое-то отношение к "переднему объявлению"?)?

10 ответов


Я думаю, что проблема здесь не в отсутствующем Include guard, а в том, что две структуры нуждаются друг в друге в своем определении. Так это типа определить проблему Ханн и яйцо.

способ решить их в C или C++ - это сделать прямые объявления по типу. Если вы скажете компилятору, что элемент является своего рода структурой, компилятор сможет сгенерировать указатель на него.

Э. Г.

внутри дерева.h:

// tell the compiler that element is a structure typedef:
typedef struct element_ element;

typedef struct tree_ tree;
struct tree_
{
    tree *first_child;
    tree *next_sibling;
    int tag;

    // now you can declare pointers to the structure.
    element *obj;
};

таким образом, вы не должны включать элемент.ч внутри дерева.больше час.

вы также должны поместить include-guards вокруг ваших заголовочных файлов.


Так в дерево.ч, вместо:

#include "element.h"

do:

typedef struct element_ element;

Это "объявляет" типы "элемент" и " struct element_ "(говорит, что они существуют), но не" определяет " их (скажем, что они есть). Все, что вам нужно хранить указатель-до-бла, что БЛА объявления не определено. Только если вы хотите уважать его (например, для чтения членов), вам нужно определение. Код в вашем ".C " файл должен это сделать, но в этом случае ваши заголовки этого не делают.

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

ответы о включают охранников ошибочны-они хорошая идея в целом, и вы должны прочитать о них и получить себе некоторые, но они не решают вашу проблему в частности.


правильный ответ-использовать include guards и использовать прямые объявления.

Включить Охранников

/* begin foo.h */
#ifndef _FOO_H
#define _FOO_H

// Your code here

#endif
/* end foo.h */

Visual C++ также поддерживает #pragma один раз. Это нестандартная директива препроцессора. В обмен на переносимость компилятора вы уменьшаете возможность коллизий имен препроцессоров и повышаете читаемость.

Вперед Деклараций

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

struct tree;    /* element.h */
struct element; /* tree.h    */

читать про вперед деклараций.

ie.


// tree.h:
#ifndef TREE_H
#define TREE_H
struct element;
struct tree
{
    struct element *obj;
    ....
};

#endif

// element.h:
#ifndef ELEMENT_H
#define ELEMENT_H
struct tree;
struct element
{
    struct tree *tree_parent;
    ...
};
#endif

Они известны как " один раз только заголовки.- Смотри.http://developer.apple.com/DOCUMENTATION/DeveloperTools/gcc-4.0.1/cpp/Once_002dOnly-Headers.html#Once_002dOnly-Headers


включить охрану полезно, но не решать проблему плаката, которая является рекурсивной зависимостью от двух структур данных.

решение здесь-объявить дерево и / или элемент как указатели на структуры в файле заголовка, поэтому вам не нужно включать .h

что-то типа:

struct element_;
typedef struct element_ element;

на вершине дерева.ч должно быть достаточно, чтобы удалить необходимость включения элемента.h

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


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

например (насколько я помню) "Объектно-Ориентированная Эвристика Дизайна" цель избежать включения охранников, потому что они маскируют только циклическую (физическую) зависимость.

другой подход предварительного декларирования структуры такой:

дерево.ч: элемент структуры _; структурное дерево_ { struct tree_* first_child; структура tree_* next_sibling; тег int; struct element_ * obj; };


Forward declaratio-это способ, с помощью которого вы можете гарантировать, что будет tyoe структуры, которая будет определена позже.


Мне не нравятся прямые объявления, потому что они избыточны и глючат. Если вы хотите, чтобы все ваши объявления были в одном месте, вы должны использовать includes и заголовочные файлы с Include guards.

вы должны думать о includes как copy-paste, когда препроцессор c находит строку #include, просто помещает все содержимое myheader.h в том же месте, где была найдена строка #include.

Ну, если вы пишете включить охранников myheader.код h будет вставлен только один раз, когда был найден первый #include.

Если ваша программа компилируется с несколькими объектными файлами, и проблема сохраняется, вы должны использовать прямые объявления между объектными файлами (это похоже на использование extern), чтобы сохранить только объявление типа для всех объектных файлов (компилятор смешивает все объявления в одной таблице и идентификаторы должны быть уникальными).


простое решение-просто не иметь отдельных файлов заголовков. В конце концов, если они зависят друг от друга, вы никогда не будете использовать одно без другого, так зачем их разделять? Вы можете жить отдельно .c файлы, которые используют один и тот же заголовок, но обеспечивают более сфокусированную функциональность.

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