Unterschied zwischen 'Struktur' und 'typedef struct' in C++?
Frage
In C++ gibt es da einen Unterschied zwischen:
struct Foo { ... };
und
typedef struct { ... } Foo;
Lösung
In C ++ gibt es nur einen feinen Unterschied. Es ist ein Überbleibsel aus C, in dem es einen Unterschied macht.
Die Sprache C-Standard ( C89 §3.1.2.3 , C99 § 6.2.3 und C11 § 6.2.3 ) Mandate eigenen Namensraum für verschiedene Kategorien von Identifikatoren, einschließlich < em> Tag-Kennungen (für struct
/ union
/ enum
) und gewöhnliche Bezeichner (für typedef
und andere Identifizierungsmerkmale).
Wenn Sie gerade gesagt:
struct Foo { ... };
Foo x;
Sie würde einen Compiler-Fehler erhalten, weil Foo
nur im Tag-Namensraum definiert ist.
würden Sie müssen erklären ihn als:
struct Foo x;
Jedes Mal, wenn Sie auf eine Foo
beziehen möchten, dann würden Sie immer es müssen nennen struct Foo
. Das nervt schnell, so können Sie eine typedef
hinzufügen:
struct Foo { ... };
typedef struct Foo Foo;
Jetzt struct Foo
(im Tag-Namespace) und einfach nur Foo
(im gewöhnlichen Bezeichner Namespace), die beide auf die gleiche Sache beziehen, und Sie können frei Objekte vom Typ Foo
ohne struct
Schlüsselwort deklarieren.
Das Konstrukt:
typedef struct Foo { ... } Foo;
ist nur eine Abkürzung für die Erklärung und typedef
.
Schließlich
typedef struct { ... } Foo;
erklärt eine anonyme Struktur und schafft eine typedef
dafür. So mit diesem Konstrukt, ist es nicht einen Namen im Tag-Namespace hat, nur einen Name in dem typedef-Namespace. Das bedeutet, es kann auch nicht die Zukunft gerichteten Begriffen erklärt. Wenn Sie eine Vorwärtsdeklaration machen wollen, haben Sie es in der Tag-Namespace einen Namen geben .
In C ++, alle struct
/ union
/ enum
/ class
Erklärungen handeln, wie sie implizit typedef
'ed werden, solange der Name nicht durch eine andere Erklärung mit dem gleichen Namen verbirgt. Siehe Michael Burr Antwort für die vollständigen Details .
Andere Tipps
dieser DDJ Artikel , Dan Saks, erklärt ein kleiner Bereich, in dem Fehler einschleichen können durch wenn Sie nicht typedef Ihre structs (und Klassen!):
Wenn Sie wollen, können Sie das C ++ vorstellen erzeugt ein typedef für jeden Tag Namen wie
typedef class string string;
Leider ist dies nicht ganz genau. Ich wünschte, es wäre so einfach, aber es ist nicht. C ++ können solche erzeugen typedefs für structs, Gewerkschaften oder Aufzählungen ohne Einführung von Inkompatibilitäten mit C.
Beispiel: Angenommen, ein C-Programm erklärt sowohl eine Funktion und eine Struktur genannt Status:
int status(); struct status;
Auch hier kann diese schlechte Praxis sein, aber es ist C. In diesem Programm-Status (durch selbst) bezieht sich auf die Funktion; struct Status bezieht sich auf die Art.
Wenn C ++ automatisch haben generieren typedefs für Tags, dann, wenn Sie Dieses Programm als C ++ kompiliert wird, die Compiler erzeugen würde:
typedef struct status status;
Leider ist dieser Typ Name würde in Konflikt mit dem Funktionsnamen und würde das Programm nicht kompilieren. das ist warum C ++ kann nicht einfach eine generieren typedef für jeden Tag.
In C ++ Tags wirken wie typedef Namen, mit der Ausnahme, dass ein Programm erklären, ein Objekt, eine Funktion oder enumerator mit dem gleichen Namen und der gleicher Umfang wie ein Tag. In diesem Fall wird die Aufgabe, Funktion oder Enumerator name versteckt den Tag-Namen. Das Programm kann beziehen sich auf den Tag-Namen nur mit das Schlüsselwort Klasse, Struktur, Vereinigung oder enum (je nachdem) vor dem Verlinke den Namen. Ein Name, Typ, bestehend aus einer dieser gefolgt von einem keywords Tag ist ein erarbeitet-Typ-Spezifizierer. Zum Beispiel struct Status und Enum Monat erarbeitet Typ-Spezifizierer.
So wird ein C-Programm, das beide enthält:
int status(); struct status;
verhält sich genauso, wenn sie als C ++ kompiliert. Der Name Status bezieht sich allein auf die Funktion. Das Programm kann auf das beziehen Typ nur durch die Verwendung von erarbeitet-type-specifier struct Status.
Wie lässt diese Fehler zu kriechen in Programme? Betrachten Sie das Programm in Listing 1 . In diesem Programm wird ein class foo mit einem Default-Konstruktor, und ein Umwandlungs Operator, wandelt ein foo Objekt char * const. Der Ausdruck
p = foo();
in Haupt sollte ein foo-Objekt erstellen und wenden Sie den Konvertierungsoperator. Das nachfolgende Ausgabeanweisung
cout << p << '\n';
sollte class foo anzuzeigen, aber es nicht. Es zeigt Funktion foo.
Dieses überraschende Ergebnis tritt auf, weil Das Programm beinhaltet Header lib.h in Listing 2 gezeigt. Dieser Header auch foo definiert eine Funktion mit dem Namen. Das Funktionsname foo versteckt den Klassennamen foo, so dass der in Bezug auf foo Haupt bezieht sich auf die Funktion, nicht die Klasse. Haupt kann nur auf die Klasse verweisen, indem Verwendung eines ausgearbeiteten-Typ-Spezifizierer, wie in
p = class foo();
Die Möglichkeit, solche Verwechslungen zu vermeiden Während des Programms ist das hinzufügen folgende typedef für die Klassennamen foo:
typedef class foo foo;
unmittelbar vor oder nach der Klasse Definition. Diese typedef bewirkt ein Konflikt zwischen dem Typnamen foo und der Name der Funktion foo (von der Bibliothek), der eine triggert Fehler bei der Kompilierung.
Ich kenne niemanden, der tatsächlich schreibt diese typedefs als selbstverständlich. Es erfordert eine Menge Disziplin. Schon seit die Häufigkeit von Fehlern, wie beispielsweise die ein in 1 Listing ist wahrscheinlich ziemlich klein, viele nie in Konflikt geraten laufen von dieses Problem. Aber wenn ein Fehler in Ihrem Software könnte zu Verletzungen führen, dann sollten Sie die typedefs schreiben nein Ganz gleich, wie unlikely den Fehler.
Ich kann mich nicht vorstellen, warum jemand würde jemals will einen Klassennamen mit einem verstecken Funktions- oder Objektnamens in der gleichen Umfang wie die Klasse. Die Ausblendregeln in C waren ein Fehler, und sie sollten nicht auf Klassen erweitert wurde C ++. Tatsächlich können Sie die richtigen Fehler, aber es erfordert zusätzliche Programmierung Disziplin und Mühe, die sollte nicht notwendig sein.
Ein weiterer wichtiger Unterschied: typedef
s kann nicht vorwärts deklariert werden. Also für die typedef
Option müssen Sie die Datei #include
die typedef
enthält, alles was bedeutet, dass Ihr #include
.h
s enthält auch die Datei, ob es sie direkt braucht oder nicht, und so weiter. Es kann auf jeden Fall Ihre Bauzeiten auf größere Projekte auswirken.
Ohne die typedef
, in einigen Fällen können Sie nur eine Vorwärtserklärung struct Foo;
an der Spitze Ihrer .h
Datei hinzufügen, und #include
nur die Struktur Definition in Ihrer .cpp
Datei.
Es ist ein Unterschied, aber subtil.Betrachten Sie es so: struct Foo
führt einen neuen Typ.Die zweite erzeugt einen alias namens Foo (und nicht ein neuer Typ) für eine Unbenannte struct
Typ.
7.1.3 Dem typedef Spezifizierer
1 [...]
Einen Namen deklariert mit dem typedef Spezifizierer wird ein typedef-Namen.Im Rahmen seiner Erklärung, eine typedef-name ist syntaktisch äquivalent zu einem Schlüsselwort und den Namen der im Zusammenhang mit dem Bezeichner in die Art und Weise, beschrieben in Klausel 8.Ein typedef-name ist also ein synonym für einen anderen Typ.Ein typedef-name nicht die Einführung einer neuen Art die Art und Weise der Deklaration einer Klasse (9.1) oder enum-Deklaration hat.
8 Wenn der typedef-Deklaration definiert eine Unbenannte Klasse (oder enum), die erste typedef-Namen deklariert die Deklaration zu dieser Klasse geben (oder enum-Typ) verwendet, um anzugeben, die Klasse geben (oder enum-Typ) für die Verknüpfung Zwecken (3.5).[ Beispiel:
typedef struct { } *ps, S; // S is the class name for linkage purposes
So, typedef immer als Platzhalter/synonym für einen anderen Typ.
Sie können nicht mit dem typedef struct verwenden Forward-Deklaration.
Die Struktur selbst ist ein anonymer Typ, so dass Sie nicht einen tatsächlichen Namen declare weiterleiten müssen.
typedef struct{
int one;
int two;
}myStruct;
Eine Vorwärtsdeklaration wie diese nicht funktionieren:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
Struct ist ein Datentyp zu erstellen. Der typedef ist einen Spitznamen für einen Datentyp eingestellt werden.
Ein wichtiger Unterschied zwischen einem 'typedef struct' und einer 'Struktur' in C ++ ist, dass Inline-Mitglied Initialisierung in 'typedef structs' wird nicht funktionieren.
// 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; };
Es gibt keinen Unterschied in C ++, aber ich glaube an C erlauben würden Sie Instanzen der struct Foo zu erklären, ohne explizit zu tun:
struct Foo bar;