Pergunta

Eu tenho alguns arquivos de cabeçalho, que se resumem a:

árvore.h:

#include "element.h"

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

e element.h:

#include "tree.h"

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

O problema é que ambos se referenciam, de modo que a árvore precisa de elemento a ser incluída e a árvore do elemento precisa ser incluída.

Isso não funciona porque, para definir a estrutura da 'árvore', a estrutura do elemento já deve ser conhecida, mas para definir a estrutura do elemento, a estrutura da árvore deve ser conhecida.

Como resolver esses tipos de loops (acho que isso pode ter algo a ver com 'declaração avançada'?)?

Foi útil?

Solução

Eu acho que o problema aqui não é o falta de guarda, mas o fato de as duas estruturas precisarem uma da outra em sua definição. Portanto, é um tipo que define o problema de Hann e ovo.

A maneira de resolvê -los em C ou C ++ é fazer declarações avançadas no tipo. Se você disser ao compilador que o elemento é uma estrutura de algum tipo, o compilador poderá gerar um ponteiro para ele.

Por exemplo

Inside 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;
};

Dessa forma, você não precisa incluir mais elemento.h dentro da árvore.h.

Você também deve colocar guardas em torno de seus arquivos de cabeçalho também.

Outras dicas

A observação crucial aqui é que o elemento não precisa conhecer a estrutura da árvore, pois apenas mantém um ponteiro. O mesmo para a árvore. Cada um precisa saber é que existe um tipo com o nome relevante, não o que há nele.

Então em árvore.h, em vez de:

#include "element.h"

Faz:

typedef struct element_ element;

Isso "declara" os tipos "elemento" e "struct element_" (diz que eles existem), mas não "os define" (diga o que são). Tudo o que você precisa para armazenar um ponteiro para Blah é que o blá é declarado, não que seja definido. Somente se você deseja deferente -o (por exemplo, para ler os membros), você precisa da definição. Código em seu arquivo ".c" precisa fazer isso, mas, neste caso, seus cabeçalhos não.

Algumas pessoas criam um único arquivo de cabeçalho, que encaminhamos todos os tipos de um cluster de cabeçalhos e, em seguida, cada cabeçalho inclui isso, em vez de descobrir quais tipos realmente precisam. Isso não é essencial nem completamente estúpido.

As respostas sobre incluir guardas estão erradas - elas são uma boa ideia em geral, e você deve ler sobre elas e obter algumas, mas elas não resolvem seu problema em particular.

A resposta correta é usar guardas e usar declarações avançadas.

Incluir guardas

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

// Your code here

#endif
/* end foo.h */

O Visual C ++ também suporta #Pragma uma vez. É uma diretiva pré -processadora não padrão. Em troca da portabilidade do compilador, você reduz a possibilidade de colisões de nomes do pré -processador e aumenta a legibilidade.

Declarações avançadas

Avançar declarar suas estruturas. Se os membros de uma estrutura ou classe não forem explicitamente necessários, você poderá declarar a existência deles no início de um arquivo de cabeçalho.

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

Ler sobre declarações avançadas.

ou seja.


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

Incluir guardas são úteis, mas não abordam o problema do pôster, que é a dependência recursiva de duas estruturas de dados.

A solução aqui é declarar árvore e/ou elemento como ponteiros para estruturas dentro do arquivo de cabeçalho, para que você não precise incluir o .h

Algo como:

struct element_;
typedef struct element_ element;

No topo da árvore.h deve ser suficiente para remover a necessidade de incluir elemento.h

Com uma declaração parcial como essa, você só pode fazer coisas com ponteiros de elementos que não exigem que o compilador saiba nada sobre o layout.

IMHO A melhor maneira é evitar esses loops, porque eles são um sinal de golpe físico que deve ser evitado.

Por exemplo (até onde eu me lembro) "Heurísticas de design orientadas a objetos" O objetivo de evitar incluir guardas porque eles apenas mascaram a dependência cíclica (física).

Uma outra abordagem é predeclar as estruturas como esta:

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

árvore.h: struct element_; struct Tree_ {struct Tree_* primeiro_child; estrutura árvore* next_sibling; tag int; struct element_ *obj; };

A Declaratio Forward é a maneira de garantir que haverá uma estrutura de estrutura que será definida posteriormente.

Não gosto de declarações avançadas porque elas são redundantes e buggy. Se você deseja todas as suas declarações no mesmo local, você deve usar e incluir arquivos de cabeçalho com guardas.

Você deve pensar que inclui como uma cópia, quando o pré-processador C encontrar uma linha #include apenas coloca todo o conteúdo do myheader.h no mesmo local em que a linha #include foi encontrada.

Bem, se você escrever incluir guardas, o código do myheader.h será colado apenas uma vez em que o primeiro #include foi encontrado.

Se o seu programa compilar com vários arquivos de objeto e o problema persistir, você deve usar as declarações avançadas entre os arquivos do objeto (é como usar externo) para manter apenas uma declaração de tipo em todos os arquivos de objeto (o compilador mistura todas as declarações na mesma tabela e identificadores devem seja único).

Uma solução simples é simplesmente não ter arquivos de cabeçalho separados. Afinal, se eles dependem um do outro, você nunca usará um sem o outro, então por que separá -los? Você pode ter arquivos .c separados que usam o mesmo cabeçalho, mas fornecem a funcionalidade mais focada.

Sei que isso não responde à pergunta de como usar todas as coisas extravagantes corretamente, mas achei útil quando estava procurando uma solução rápida para um problema semelhante.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top