12

要約すると、次のヘッダーファイルがいくつかあります。

ツリー.h:

#include "element.h"

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

および element.h:

#include "tree.h"

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

問題は、両者が相互に参照しているため、ツリーに要素を含める必要があり、要素にツリーを含める必要があることです。

「ツリー」構造を定義するには、要素構造が既知である必要がありますが、要素構造を定義するには、ツリー構造が既知である必要があるため、これは機能しません。

これらのタイプのループを解決するにはどうすればよいですか (これは「前方宣言」と関係があるのではないでしょうか?)。

4

11 に答える 11

29

ここでの問題は、インクルード ガードの欠落ではなく、2 つの構造体の定義で互いが必要であるという事実だと思います。型定義のハンと卵の問題です。

C または C++ でこれらを解決する方法は、型の前方宣言を行うことです。要素が何らかの構造体であることをコンパイラに伝えると、コンパイラはそれへのポインタを生成できます。

例えば

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

そうすれば、tree.h 内に element.h を含める必要がなくなります。

また、ヘッダー ファイルにもインクルード ガードを配置する必要があります。

于 2008-09-28T20:39:24.727 に答える
9

ここで重要なことは、要素はツリーへのポインタを保持するだけなので、ツリーの構造を知る必要がないということです。木も同じ。それぞれが知る必要があるのは、その中に何が含まれているかではなく、関連する名前の型が存在することだけです。

したがって、tree.h では、次の代わりに:

#include "element.h"

行う:

typedef struct element_ element;

これは型「element」と「struct element_」を「宣言」しますが (それらが存在することを示します)、それらを「定義」しません (それらが何であるかを示します)。blah へのポインターを格納するために必要なのは、blah が定義されていることではなく、blah が宣言されていることだけです。それを参照したい場合 (メンバーを読み取る場合など) にのみ、定義が必要です。「.c」ファイルのコードはそれを行う必要がありますが、この場合、ヘッダーは必要ありません。

一部の人々は、ヘッダーのクラスター内のすべてのタイプを前方宣言する単一のヘッダー ファイルを作成し、各ヘッダーにそれを含めます。実際に必要なタイプを特定するのではありません。それは本質的でも完全にばかげているわけでもありません。

インクルードガードに関する答えは間違っています - それらは一般的には良い考えであり、あなたはそれらについて読んで自分でいくつか手に入れる必要がありますが、それらは特にあなたの問題を解決しません.

于 2008-09-28T20:38:49.113 に答える
3

正解は、インクルード ガードを使用し、前方宣言を使用することです。

ガードを含める

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

// Your code here

#endif
/* end foo.h */

Visual C++ は #pragma once もサポートしています。非標準のプリプロセッサ ディレクティブです。コンパイラの移植性と引き換えに、プリプロセッサ名の衝突の可能性を減らし、読みやすさを向上させます。

前方宣言

構造体を前方宣言します。構造体またはクラスのメンバーが明示的に必要でない場合は、ヘッダー ファイルの先頭でそれらの存在を宣言できます。

struct tree;    /* element.h */
struct element; /* tree.h    */
于 2008-09-28T20:31:15.370 に答える
2

前方宣言について読んでください。

すなわち。


// 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
于 2008-09-28T20:39:22.073 に答える
0

これらは「1 回限りのヘッダー」として知られています。http://developer.apple.com/DOCUMENTATION/DeveloperTools/gcc-4.0.1/cpp/Once_002dOnly-Headers.html#Once_002dOnly-Headersを参照してください。

于 2008-09-28T20:30:12.517 に答える
0

冗長でバグがあるため、前方宣言は好きではありません。すべての宣言を同じ場所に配置したい場合は、インクルード ガード付きのインクルード ファイルとヘッダー ファイルを使用する必要があります。

インクルードは、コピー アンド ペーストとして考える必要があります。c プリプロセッサが #include 行を見つけた場合、myheader.h のコンテンツ全体を #include 行が見つかったのと同じ場所に配置するだけです。

インクルード ガードを記述した場合、myheader.h のコードは、最初の #include が見つかった場所に 1 回だけ貼り付けられます。

プログラムが複数のオブジェクト ファイルでコンパイルされ、問題が解決しない場合は、すべてのオブジェクト ファイルに対して型宣言のみを保持するために、オブジェクト ファイル間で前方宣言を使用する必要があります (extern を使用するようなものです) (コンパイラは同じテーブル内のすべての宣言を混合し、識別子は一意であること)。

于 2010-11-13T00:52:14.057 に答える
0

インクルード ガードは便利ですが、2 つのデータ構造への再帰的な依存関係である投稿者の問題には対処しません。

ここでの解決策は、ツリーや要素をヘッダー ファイル内の構造体へのポインターとして宣言することです。そのため、.h を含める必要はありません。

何かのようなもの:

struct element_;
typedef struct element_ element;

element.h を含める必要をなくすには、tree.h の先頭に十分なはずです。

このような部分的な宣言では、コンパイラがレイアウトについて何も知る必要のない要素ポインターを使用することしかできません。

于 2008-09-28T20:41:40.587 に答える
0

私見の最善の方法は、そのようなループを回避することです。これは、回避する必要がある物理的なクーピングの兆候であるためです。

たとえば、(私が覚えている限り) 「オブジェクト指向設計ヒューリスティックス」は、循環 (物理) 依存関係のみをマスクするため、インクルード ガードを回避することを目的としています。

別のアプローチは、次のように構造体を事前宣言することです。

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

于 2008-09-28T20:46:12.743 に答える
0

簡単な解決策は、個別のヘッダー ファイルを持たないことです。結局のところ、それらが互いに依存している場合、一方を他方なしで使用することは決してないので、なぜそれらを分離するのでしょうか? 同じヘッダーを使用するが、より焦点を絞った機能を提供する別々の .c ファイルを持つことができます。

これがすべての凝ったものを正しく使用する方法の質問に答えないことは知っていますが、同様の問題に対する簡単な修正を探していたときに役立ちました.

于 2017-04-03T03:36:05.907 に答える
0

前方宣言は、後で定義される構造のタイプがあることを保証できる方法です。

于 2009-10-06T11:15:58.637 に答える