C ++の「struct」と「typedef struct」の違いは?
質問
C ++では、次の間に違いがあります:
struct Foo { ... };
and
typedef struct { ... } Foo;
解決
C ++では、わずかな違いしかありません。それは違いを生むCからのホールドオーバーです。
C言語標準( C89§ 3.1.2.3 、 C99§ 6.2.3 、および C11§ 6.2.3 ) タグ識別子( struct
/ union
/ enum
の場合)およびを含む、識別子のさまざまなカテゴリの名前空間通常の識別子( typedef
およびその他の識別子用)。
先ほど言った場合:
struct Foo { ... };
Foo x;
Foo はタグ名前空間でのみ定義されているため、コンパイラエラーが発生します。
次のように宣言する必要があります:
struct Foo x;
Foo
を参照したいときは、常に struct Foo
と呼ぶ必要があります。これはいらいらするので、 typedef
を追加できます:
struct Foo { ... };
typedef struct Foo Foo;
Now struct Foo
(タグ名前空間内)と単なる Foo
(通常の識別子名前空間内)はどちらも同じものを参照し、オブジェクトを自由に宣言できます struct
キーワードなしのタイプ Foo
の。
構成:
typedef struct Foo { ... } Foo;
は、宣言と typedef
の略語です。
最後に、
typedef struct { ... } Foo;
匿名構造を宣言し、そのための typedef
を作成します。したがって、この構造では、タグの名前空間には名前がなく、typedef名前空間に名前のみがあります。これは、前方宣言もできないことを意味します。 前方宣言を行う場合は、タグの名前空間で名前を指定する必要があります。
C ++では、すべての struct
/ union
/ enum
/ class
宣言は、暗黙的に名前が同じ名前の別の宣言によって隠されていない限り、> typedef
'ed。詳細については、 Michael Burrの回答をご覧ください。 。
他のヒント
このDDJの記事で、ダンサックスはバグが忍び寄る小さな領域について説明しています構造体(およびクラス!)をtypedefしない場合:
必要に応じて、C ++ すべてのタグのtypedefを生成します
などの名前typedef class string string;
残念ながら、これは完全ではありません 正確。こんなに簡単だったらいいのに、 しかし、そうではありません。 C ++はそのようなものを生成できません 構造体、共用体、または列挙型のtypedef 非互換性を導入することなく Cで。
たとえば、Cプログラムを想定します 関数と構造体の両方を宣言します 名前付きステータス:
int status(); struct status;
これも悪い習慣かもしれませんが、 Cです。このプログラムでは、ステータス(by 自体)関数を指します。構造 ステータスはタイプを指します。
C ++が自動的に生成した場合 タグのtypedef、その後 このプログラムをC ++としてコンパイルし、 コンパイラは以下を生成します:
typedef struct status status;
残念ながら、この型名は 関数名と競合する プログラムはコンパイルされません。それは C ++が単純に生成できない理由 各タグのtypedef。
C ++では、タグはtypedefのように機能します プログラムができることを除いて、名前 オブジェクト、関数を宣言する、または 同じ名前の列挙子 タグと同じスコープ。その場合、 オブジェクト、関数、または列挙子名 タグ名を非表示にします。プログラムは を使用してのみタグ名を参照する キーワードクラス、構造体、ユニオン、または 列挙型(必要に応じて)の前に タグ名。で構成される型名 これらのキーワードの1つに続けて tagは精巧なタイプ指定子です。 たとえば、struct statusおよびenum 月は精巧なタイプ指定子です。
したがって、両方を含むCプログラム:
p = foo();
は、C ++としてコンパイルされた場合と同じように動作します。 名前のステータスのみが 関数。プログラムは を使用してのみ入力します エラボレートされた型指定子構造体 ステータス。
それで、これはどのようにしてバグを忍び込ませますか プログラムに?のプログラムを検討してください リスト1 。このプログラムは デフォルトのコンストラクタを持つクラスfoo、 および変換演算子 fooオブジェクトをchar const *に変換します。 式
メインのcout << p << '\n';
はfooオブジェクトを構築する必要があります 変換演算子を適用します。の 後続の出力ステートメント
p = class foo();
クラスfooを表示する必要がありますが、 しません。関数fooを表示します。
この驚くべき結果は、 プログラムにはヘッダーlib.hが含まれています リスト2 に示されています。このヘッダー fooという名前の関数を定義します。の 関数名fooはクラス名を隠します foo、したがってmainのfooへの参照 クラスではなく関数を参照します。 mainは以下によってのみクラスを参照できます 詳細なタイプ指定子を使用して、 で
typedef class foo foo;
このような混乱を避ける方法 プログラム全体で追加することです クラス名のtypedefに続く foo:
<*>クラスの直前または直後 定義。このtypedefにより、 タイプ名fooと 関数名foo(から ライブラリ) コンパイル時エラー。
実際に書いている人は誰もいません 当然これらのtypedef。 それには多くの訓練が必要です。以来 などのエラーの発生率 リスト1 の1つはおそらくきれいです 小さい、あなたの多くは決して汚さない この問題。しかし、あなたのエラー ソフトウェアは人身傷害を引き起こす可能性があり、 その後、typedefsを書く必要があります エラーがどれほど起こりにくいかは関係ありません。
なぜ誰もがそうするのか想像できない クラス名を隠したい 関数またはオブジェクト
もう1つの重要な違い: typedef
は前方宣言できません。したがって、 typedef
オプションでは、 typedef
を含むファイルを #include
する必要があります。つまり、 #include
.h
には、直接必要であるかどうかに関係なく、そのファイルも含まれます。大きなプロジェクトのビルド時間に確実に影響します。
typedef
なしで、場合によっては struct Foo;
の前方宣言を .h
ファイルの先頭に追加できます、および .cpp
ファイル内の構造体定義のみ #include
。
違いは ありますが、微妙です。次のように見てください: struct Foo
は新しい型を導入します。 2番目のものは、名前のない struct
タイプのFoo(新しいタイプではない)と呼ばれるエイリアスを作成します。
7.1.3 typedef指定子
1 [...]
typedef指定子で宣言された名前はtypedef-nameになります。その宣言の範囲内で、 typedef-nameは、構文的にはキーワードと同等であり、識別子に関連付けられている型に名前を付けます したがって、typedef-nameは、別の型の同義語です。 typedef-nameは、 クラス宣言(9.1)またはenum宣言とは異なり、新しい型を導入しません 。
8 typedef宣言で名前のないクラス(または列挙型)が定義されている場合、宣言によって宣言された最初のtypedef-name そのクラス型(または列挙型)であることは、リンケージのクラス型(または列挙型)を示すために使用されます 目的のみ(3.5)。 [例:
typedef struct { } *ps, S; // S is the class name for linkage purposes
したがって、typedef always は、別のタイプのプレースホルダー/シノニムとして使用されます。
typedef構造体では前方宣言を使用できません。
構造体自体は匿名型なので、実際に宣言する名前はありません。
typedef struct{
int one;
int two;
}myStruct;
このような前方宣言は機能しません:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
構造は、データ型を作成することです。 typedefは、データ型のニックネームを設定することです。
C ++の「typedef struct」と「struct」の重要な違いは、「typedef structs」のインラインメンバーの初期化が機能しないことです。
// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;
// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
C ++には違いはありませんが、Cでは明示的に実行しなくてもstruct Fooのインスタンスを宣言できると信じています:
struct Foo bar;