Question

En C ++, existe-t-il une différence entre:

struct Foo { ... };

et

typedef struct { ... } Foo;
Était-ce utile?

La solution

En C ++, il n’ya qu’une différence subtile. Il s’agit d’un reliquat de C, dans lequel cela fait une différence.

La norme de langage C ( C89 §3.1.2.3 , C99 §6.2.3 et C11 §6.2.3 ) requiert des espaces de nom distincts pour différentes catégories d'identificateurs, y compris < em> identificateurs de balise (pour struct / union / enum ) et identificateurs ordinaires (pour < code> typedef et d'autres identificateurs).

Si vous venez de dire:

struct Foo { ... };
Foo x;

vous obtiendrez une erreur de compilation, car Foo n'est défini que dans l'espace de nom de la balise.

Vous devez le déclarer comme:

struct Foo x;

Chaque fois que vous souhaitez faire référence à un Foo , vous devez toujours l'appeler une struct Foo . Cela devient vite agaçant, vous pouvez donc ajouter un typedef :

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

Maintenant, struct Foo (dans l'espace de noms de balise) et simplement Foo (dans l'espace de noms d'identificateur ordinaire) se réfèrent tous deux à 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;

est simplement une abréviation de la déclaration et typedef .

Enfin,

typedef struct { ... } Foo;

déclare une structure anonyme et crée un typedef pour celle-ci. Ainsi, avec cette construction, elle n’a pas de nom dans l’espace de noms de balises, mais seulement dans l’espace de noms typedef. Cela signifie qu'il ne peut pas non plus être déclaré en avant. Si vous souhaitez faire une déclaration, vous devez lui attribuer un nom dans l'espace de noms de balise .

En C ++, toutes les déclarations struct / union / enum / class agissent comme si elles étaient implicitement typedef 'ed, tant que le nom n'est pas masqué par une autre déclaration portant le même nom. Voir la réponse de Michael Burr pour plus de détails. .

Autres conseils

Dans cet article de DDJ , Dan Saks explique un petit domaine dans lequel des bogues peuvent se glisser si vous ne tapez pas vos structures (et vos classes!):

  

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

typedef class string string;
     

Malheureusement, ce n'est pas tout à fait   précis. J'aimerais que ce soit aussi simple   mais ce n'est pas. C ++ ne peut pas générer une telle   typedefs pour structs, unions ou enums   sans introduire d'incompatibilités   avec C.

     

Par exemple, supposons un programme C   déclare à la fois une fonction et une structure   statut nommé:

int status(); struct status;
     

Encore une fois, cela peut être une mauvaise pratique, mais   c. Dans ce programme, status (par   lui-même) se réfère à la fonction; struct   le statut fait référence au type.

     

Si C ++ a généré automatiquement   typedefs pour les tags, puis quand vous   compilé ce programme en C ++, le   le compilateur générerait:

typedef struct status status;
     

Malheureusement, ce nom de type serait   conflit avec le nom de la fonction, et   le programme ne compilerait pas. C'est   pourquoi C ++ ne peut pas simplement générer un   typedef pour chaque tag.

     

En C ++, les balises agissent de la même manière que typedef   noms, sauf qu'un programme peut   déclarer un objet, une fonction ou   recenseur du même nom et le   même portée qu'une balise. Dans ce cas, le   nom d'objet, de fonction ou d'énumérateur   cache le nom de la balise. Le programme peut   faire référence au nom de la balise uniquement en utilisant   le mot-clé class, struct, union ou   énumération (selon le cas) devant le   nom de tag. 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, struct status et enum   mois sont des spécificateurs de type élaboré.

     

Ainsi, un programme C contenant à la fois:

p = foo();
     

se comporte de la même manière lors de la compilation en C ++.   Le statut de nom fait référence à la seule   une fonction. Le programme peut faire référence à la   tapez uniquement en utilisant le   structure de spécificateur de type élaboré   statut.

     

Alors, comment cela permet-il aux insectes de se faufiler?   dans les programmes? Considérez le programme en    Liste 1 . Ce programme définit un   classe foo avec un constructeur par défaut,   et un opérateur de conversion qui   convertit un objet foo en char const *.   L'expression

cout << p << '\n';
     

in main devrait construire un objet foo   et appliquez l'opérateur de conversion. le   déclaration de sortie suivante

p = class foo();
     

devrait afficher la classe foo, mais cela   ne le fait pas Il affiche la fonction foo.

     

Ce résultat surprenant est dû au fait que   le programme comprend en-tête lib.h   indiqué dans la Liste 2 . Cet en-tête   définit une fonction également appelée foo. le   nom de fonction foo cache le nom de la classe   foo, donc la référence à foo dans main   fait référence à la fonction, pas à la classe.   principal peut se référer à la classe que par   utilisant un spécificateur de type élaboré, comme   dans

typedef class foo foo;
     

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

<*>      

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

     

Je ne connais personne qui écrit réellement   ces typefefs comme une évidence.   Cela demande beaucoup de discipline. Puisque   l'incidence d'erreurs telles que la   un dans la Liste 1 est probablement jolie   petit, vous ne courez jamais beaucoup   ce problème. Mais si une erreur dans votre   le logiciel pourrait causer des dommages corporels,   alors vous devriez écrire les typedefs non   Peu importe comment peu probable l'erreur.

     

Je ne peux pas imaginer pourquoi quelqu'un voudrait jamais   vouloir cacher un nom de classe avec un   nom de fonction ou d'objet dans le même   portée comme la classe. Les règles de la dissimulation   en C étaient une erreur, et ils devraient   pas été étendu aux classes de   C ++. En effet, vous pouvez corriger le   erreur, mais il faut extra   programmation de la discipline et des efforts qui   ne devrait pas être nécessaire.

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

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

Il existe une différence, mais subtile. Regardez-le 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 struct non nommé .

  

7.1.3 Spécificateur de typedef

     

1 [...]

     

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

     

8 Si la déclaration typedef définit une classe (ou un enum) non nommée, le premier nom-typedef déclaré par la déclaration   être ce type de classe (ou type enum) est utilisé pour désigner le type de classe (ou type enum) pour la liaison   uniquement à des fins (3.5). [Exemple:

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

Ainsi, un typedef toujours est utilisé comme espace réservé / synonyme pour un autre type.

Vous ne pouvez pas utiliser la déclaration anticipée avec la structure typedef.

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

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

Une déclaration anticipée comme celle-ci ne fonctionnera pas:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

Struct consiste à créer un type de données. Le typedef doit définir un pseudonyme pour un type de données.

Une différence importante entre une structure 'typedef' et une 'structure' en C ++ réside dans le fait que l'initialisation des membres en ligne dans les 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; };

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

struct Foo bar;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top