Différence entre 'struct' et 'typedef struct' en C++?

en C++, y a-t-il une différence entre:

struct Foo { ... };

et

typedef struct { ... } Foo;
697
demandé sur criddell 2009-03-04 23:41:12

8 réponses

en C++, il n'y a qu'une différence subtile. C'est un hold-over de C, dans lequel ça fait une différence.

la norme linguistique C ( C89 §3.1.2.3 , c99 §6.2.3 , et C11 §6.2.3 ) prescrit des espaces de noms distincts pour différentes catégories d'identificateurs, y compris identificateurs d'étiquette (pour struct / union / enum ) et ordinaire identificateurs (pour typedef et autres identificateurs).

si vous venez de dire:

struct Foo { ... };
Foo x;

vous obtiendriez une erreur de compilateur, parce que Foo n'est défini que dans l'Espace-nom de la balise.

vous devez le déclarer comme:

struct Foo x;

chaque fois que vous voulez faire référence à un Foo , vous devez toujours l'appeler un struct Foo . Cela devient vite ennuyeux, de sorte que vous pouvez ajouter un typedef :

struct Foo { ... };
typedef struct Foo Foo;

maintenant struct Foo (dans l'Espace-nom de l'étiquette) et tout simplement Foo (dans l'Espace-nom de l'identificateur ordinaire) font tous deux référence à la même chose, et vous pouvez librement déclarer des objets de type Foo sans le mot-clé struct .


La construction:

typedef struct Foo { ... } Foo;

n'est qu'une abréviation pour la déclaration et typedef .


enfin,

typedef struct { ... } Foo;

déclare une structure anonyme et crée un typedef pour elle. Ainsi, avec cette construction, il n'a pas de nom dans l'Espace-nom de la balise, seulement un nom dans l'Espace-nom de typedef. Cela signifie qu'elle ne peut pas non plus être transmise. si vous voulez faire une déclaration forward, vous devez lui donner un nom dans l'espace de nom d'étiquette .


en C++, Tous struct / union / enum / class les déclarations agissent comme si elles étaient implicitement typedef 'ed, à condition que le nom ne soit pas caché par une autre déclaration portant le même nom. Voir réponse de Michael Burr pour plus de détails.

1043
répondu Adam Rosenfield 2018-07-14 17:08:20

Dans ce DDJ l'article , Dan Saks explique qu'une petite zone où les punaises peuvent se glisser à travers si vous n'avez pas de typedef vos structures (et les classes!):

si vous voulez, vous pouvez imaginer que C++ génère un typedef pour chaque balise nom, tel que

typedef class string string;

Malheureusement, ce n'est pas entièrement précis. Je voudrais que ce soit si simple, mais il n'est pas. C++ ne peut pas générer typedefs pour les structures, les syndicats, ou les énumérations sans introduire d'incompatibilités avec C.

par exemple, supposons un programme C déclare à la fois une fonction et une structure État nommé:

int status(); struct status;

encore une fois, ce peut être une mauvaise pratique, mais c'est C. Dans ce programme, l'état (par se réfère à la fonction; struct le statut fait référence à la nature.

si C++ a généré automatiquement tapedefs pour tags, puis quand vous compilé cette programme en C++, l' compilateur générerait:

typedef struct status status;

malheureusement, ce type de nom en conflit avec le nom de la fonction, et le programme ne compile pas. C'est pourquoi C++ ne peut pas simplement générer Dactylographiez pour chaque étiquette.

en C++, les tags agissent exactement comme typedef les noms, sauf qu'un programme peut déclarer un objet, une fonction ou énumérateur avec le même nom et la même portée que d'une balise. Dans ce cas, l' objet, la fonction, ou le nom de l'agent recenseur cache le nom de la balise. Le programme peut se référer au nom de la balise uniquement en utilisant la classe de mots-clés, struct, union, or enum (le cas échéant) devant le nom de la balise. Un nom de type composé de un de ces mots-clés suivi d'un tag est un spécificateur de type élaboré. Par exemple, l'état de la structure et l'enum mois sont élaborés-les spécificateurs de type.

ainsi, un programme C qui contient à la fois:

int status(); struct status;

se comporte de la même façon lorsqu'il est compilé en C++. Le seul Statut du nom fait référence à la fonction. Le programme peut se référer à tapez seulement en utilisant le élaboré de type spécificateur de struct statut.

alors comment cela permet-il aux bogues de glisser dans les programmes? Considérer le programme dans Liste 1 . Ce programme définit un la classe foo avec un constructeur par défaut, et un opérateur de conversion qui convertit un objet foo en char const *. Expression

p = foo();

doit construire un objet foo et d'appliquer l'opérateur de conversion. Le état des résultats ultérieurs

cout << p << '\n';

devrait afficher la classe foo, mais il Non. Il affiche la fonction foo.

ce résultat surprenant se produit parce que le programme inclut l'en-tête lib.h indiqué dans et 2 . Cet en-tête définit une fonction nommée foo. Le nom de la fonction toto se cache le nom de la classe foo, donc la référence à foo in main fait référence à la fonction, pas à la classe. principale peut se référer à la classe uniquement en utilisant un spécificateur de type élaboré, comme in

p = class foo();

La manière d'éviter une telle confusion tout au long du programme est d'ajouter la suivant typedef pour le nom de classe foo:

typedef class foo foo;

immédiatement avant ou après la classe définition. Ce typedef provoque une les conflits entre les nom du type foo et le nom de la fonction foo (du bibliothèque) qui déclenchera un erreur de compilation.

Je ne connais personne qui écrit ces typedefs comme une question de cours. Cela demande beaucoup de discipline. Depuis l'incidence d'erreurs telles que un dans Liste 1 est probablement jolie petite, vous ne jamais aller à l'encontre de ce problème. Mais si une erreur dans votre le logiciel peut causer des lésions corporelles, alors tu devrais écrire les typedefs pas importe combien peu probable de l'erreur.

Je ne peux pas imaginer pourquoi quelqu'un pourrait jamais souhaitez masquer un nom de classe avec un la fonction ou le nom de l'objet dans le même portée de la classe. Les règles de dissimulation en C étaient une erreur, et ils devraient n'ont pas été étendues aux classes C.++ En effet, vous pouvez corriger la erreur, mais il nécessite un supplément la discipline de programmation et l'effort qui ne devrait pas être nécessaire.

209
répondu Michael Burr 2013-12-19 18:22:10

une autre différence importante: typedef s ne peut pas être avancé. Donc, pour l'option typedef vous devez #include le fichier contenant le typedef , ce qui signifie Tout ce que #include s votre .h inclut également ce fichier, qu'il en ait besoin ou non, et ainsi de suite. Cela peut certainement avoir un impact sur vos temps de construction sur les projets plus importants.

Sans le typedef , dans certains cas, vous pouvez simplement ajouter une déclaration de struct Foo; en haut de votre fichier .h , et seulement #include la définition de la structure dans votre fichier .cpp .

58
répondu Joe 2013-12-19 18:21:35

est une différence, mais subtile. Regardons cela de cette façon: struct Foo introduit un nouveau type. Le second crée un alias appelé Foo (et non un nouveau type) pour un type sans nom struct .

7.1.3 le spécificateur de type

1 [...]

un nom déclaré avec le spécificateur typedef devient un typedef-name. Dans le cadre de ses déclaration, un typedef-name est syntaxiquement équivalent à un mot-clé et nomme le type associé à l'identificateur dans de la manière décrite à l'article 8. Un nom typedef est donc synonyme d'un autre type. Un nom de type n'introduit pas un nouveau type comme le fait une déclaration de classe (9.1) ou une déclaration enum.

8 si la déclaration de typedef définit une classe non dénommée (ou enum), le premier nom de typedef déclaré par déclaration le type de classe (ou le type enum) est utilisé pour désigner le type de classe (ou le type enum) pour la liaison seulement (3.5). [ Exemple:

typedef struct { } *ps, S; // S is the class name for linkage purposes

ainsi, un typedef toujours est utilisé comme un attribut/synonyme pour un autre type.

29
répondu dirkgently 2009-03-04 22:01:13

vous ne pouvez pas utiliser la déclaration forward avec la structure typedef.

la structure elle-même est un type anonyme, donc vous n'avez pas de nom réel à transmettre déclarer.

typedef struct{
    int one;
    int two;
}myStruct;

Une déclaration anticipée comme cela ne marchera pas:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
10
répondu Yochai Timmer 2013-08-05 09:18:39

une différence importante entre une' structure typedef 'et une' structure 'en C++ est que l'initialisation des membres en ligne dans' structures typedef ' ne fonctionnera pas.

// 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; };
0
répondu user2796283 2016-04-11 13:26:25

Struct est de créer un type de données. Le typedef est de définir un surnom pour un type de données.

-1
répondu David Tu 2017-04-08 03:06:14

il n'y a pas de différence en C++, mais je crois en C qu'il vous permettrait de déclarer des instances de la struct Foo sans faire explicitement:

struct Foo bar;
-2
répondu xian 2009-03-04 20:44:20