Question

J'ai quelques fichiers d'en-tête, qui se résument à:

tree.h:

#include "element.h"

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

et element.h:

#include "tree.h"

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

Le problème est qu’ils se référencent tous les deux. Il est donc nécessaire que l’élément tree soit inclus, et cet élément nécessite l’inclusion de tree.

Cela ne fonctionne pas car pour définir la structure en arborescence, la structure de l'élément doit déjà être connue, mais pour définir la structure de l'élément, la structure en arbre doit être connue.

Comment résoudre ces types de boucles (je pense que cela a peut-être quelque chose à voir avec la "déclaration anticipée"?)?

Était-ce utile?

La solution

Je pense que le problème ici n’est pas la disparition de garde, mais le fait que les deux structures ont besoin de l’une de l’autre dans leur définition. Donc, c’est un problème de définition du type œnn et hann.

Pour résoudre ces problèmes en C ou C ++, vous devez effectuer des déclarations en aval sur le type. Si vous indiquez au compilateur que cet élément est une structure, il est capable de générer un pointeur sur celle-ci.

ex.

À l'intérieur de tree.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;
};

De cette façon, vous ne devez plus inclure element.h dans tree.h.

Vous devriez également inclure des gardes d'inclusion autour de vos fichiers d'en-tête.

Autres conseils

L’observation cruciale ici est que l’élément n’a pas besoin de connaître la structure de l’arbre, car il ne contient qu’un pointeur. La même chose pour l'arbre. Tout ce qu’il faut savoir, c’est qu’il existe un type portant le nom correspondant, et non son contenu.

Donc dans tree.h, au lieu de:

#include "element.h"

faire:

typedef struct element_ element;

Cette " déclare " les types " element " et " struct element_ " (dit qu'ils existent), mais ne définit pas " eux (dites ce qu'ils sont). Tout ce dont vous avez besoin pour stocker un pointeur sur bla est que blah soit déclaré, pas qu'il soit défini. Vous avez besoin de la définition uniquement si vous souhaitez la respecter (par exemple pour lire les membres). Code dans votre " .c " fichier doit le faire, mais dans ce cas, vos en-têtes ne le font pas.

Certaines personnes créent un fichier d’en-tête unique qui déclare tous les types d’un cluster d’en-têtes, puis chaque en-tête l’inclut au lieu de déterminer les types dont il a réellement besoin. Ce n'est ni essentiel ni complètement stupide.

Les réponses concernant les gardes sont fausses. C’est une bonne idée en général. Vous devriez les lire et en obtenir vous-même, mais elles ne résolvent pas votre problème en particulier.

La bonne réponse consiste à utiliser les gardes d'inclusion et les déclarations en aval.

Inclure les gardes

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

// Your code here

#endif
/* end foo.h */

Visual C ++ prend également en charge #pragma une fois. C'est une directive de préprocesseur non standard. En échange de la portabilité du compilateur, vous réduisez le nombre de conflits de noms de préprocesseurs et améliorez la lisibilité.

Déclarations avancées

Déclarez en avant vos structs. Si les membres d'une structure ou d'une classe ne sont pas explicitement nécessaires, vous pouvez déclarer leur existence au début d'un fichier d'en-tête.

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

En savoir plus sur les déclarations d'avance .

c'est-à-dire


// 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

Inclure les gardes est utile, mais ne résolvez pas le problème de l'affiche, à savoir la dépendance récursive à deux structures de données.

La solution ici est de déclarer l'arbre et / ou l'élément en tant que pointeurs vers des structures dans le fichier d'en-tête, de sorte que vous n'avez pas besoin d'inclure le .h

Quelque chose comme:

struct element_;
typedef struct element_ element;

Au sommet de tree.h devrait suffire à supprimer la nécessité d'inclure element.h

Avec une déclaration partielle comme celle-ci, vous pouvez uniquement utiliser des pointeurs d’éléments qui ne nécessitent pas que le compilateur connaisse la mise en page.

À mon humble avis, le mieux est d’éviter de telles boucles, car elles sont un signe de coup physique à éviter.

Par exemple (autant que je me souvienne), "Heuristique de conception orientée objet". but d'éviter Inclure les gardes car ils masquent uniquement la dépendance cyclique (physique).

Une autre approche consiste à prédéclarer les structures de la manière suivante:

element.h:
struct tree_;
struct element_
  {
    struct tree_ *tree_parent;
    char *name;
  };

tree.h: struct element_; struct tree_ { struct tree_* first_child; struct tree_* next_sibling; int tag; struct element_ *obj; };

La déclaration anticipée est le moyen par lequel vous pouvez garantir qu’un type de structure sera défini ultérieurement.

Je n'aime pas les déclarations en aval, car elles sont redondantes et boguées. Si vous souhaitez que toutes vos déclarations se trouvent au même endroit, vous devez utiliser les fichiers includes et header avec les gardes include.

Vous devriez considérer les inclusions comme un copier-coller lorsque le préprocesseur c trouve une ligne #include place tout le contenu de myheader.h au même endroit que celui où la ligne #include a été trouvée.

Eh bien, si vous écrivez avec des gardes, le code de myheader.h ne sera collé qu'une seule fois où le premier #include a été trouvé.

Si votre programme compile avec plusieurs fichiers objet et que le problème persiste, vous devez utiliser des déclarations en aval entre fichiers (comme si vous utilisiez extern) afin de ne conserver qu'une déclaration de type dans tous les fichiers objet (le compilateur mélange toutes les déclarations dans le même tableau). et les identifiants doivent être uniques).

Une solution simple consiste à ne pas créer de fichiers d’en-tête séparés. Après tout, s’ils dépendent les uns des autres, ils n’utiliseront jamais l’un sans l’autre, alors pourquoi les séparer? Vous pouvez avoir des fichiers .c séparés qui utilisent tous les deux le même en-tête mais fournissent les fonctionnalités les plus ciblées.

Je sais que cela ne répond pas à la question de savoir comment utiliser correctement tous les éléments fantaisistes, mais cela m'a été utile lorsque je cherchais une solution rapide à un problème similaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top