Pregunta

En C ++, ¿hay alguna diferencia entre:

struct Foo { ... };

y

typedef struct { ... } Foo;
¿Fue útil?

Solución

En C ++, solo hay una diferencia sutil. Es un remanente de C, en el que hace una diferencia.

El estándar de lenguaje C ( C89 §3.1.2.3 , C99 §6.2.3 , y C11 §6.2.3 ) exige espacios de nombres separados para diferentes categorías de identificadores, incluido < em> identificadores de etiquetas (para struct / union / enum ) y identificadores ordinarios (para < código> typedef y otros identificadores).

Si acabas de decir:

struct Foo { ... };
Foo x;

obtendrías un error de compilación, porque Foo solo se define en el espacio de nombres de la etiqueta.

Deberías declararlo como:

struct Foo x;

Cada vez que quieras referirte a un Foo , siempre deberías llamarlo struct Foo . Esto se vuelve molesto rápidamente, por lo que puedes agregar un typedef :

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

Ahora struct Foo (en el espacio de nombres de la etiqueta) y simplemente Foo (en el espacio de nombres del identificador ordinario) se refieren a la misma cosa, y puede declarar objetos libremente del tipo Foo sin la palabra clave struct .


La construcción:

typedef struct Foo { ... } Foo;

es solo una abreviatura de la declaración y typedef .


Finalmente,

typedef struct { ... } Foo;

declara una estructura anónima y crea un typedef para ella. Por lo tanto, con esta construcción, no tiene un nombre en el espacio de nombres de la etiqueta, solo un nombre en el espacio de nombres typedef. Esto significa que tampoco se puede declarar hacia adelante. Si desea hacer una declaración de reenvío, debe darle un nombre en el espacio de nombres de la etiqueta .


En C ++, todas las declaraciones de struct / union / enum / class actúan como si estuvieran implícitamente en typedef 'ed, siempre que el nombre no esté oculto por otra declaración con el mismo nombre. Consulte la la respuesta de Michael Burr para los detalles completos .

Otros consejos

En este artículo de DDJ , Dan Saks explica un área pequeña donde los errores pueden pasar. Si no tecleas tus estructuras (¡y clases!):

  

Si quieres, puedes imaginar que C ++   Genera un typedef para cada etiqueta.   nombre, como

typedef class string string;
     

Lamentablemente, esto no es del todo   preciso. Ojalá fuera tan simple,   pero no lo es. C ++ no puede generar tales   typedefs para estructuras, uniones o enumeraciones   sin introducir incompatibilidades   con C.

     

Por ejemplo, supongamos un programa en C   declara tanto una función como una estructura   estado nombrado:

int status(); struct status;
     

Una vez más, esto puede ser una mala práctica, pero   es C. En este programa, el estado (por   sí) se refiere a la función; estructura   el estado se refiere al tipo.

     

Si C ++ generara automáticamente   typedefs para etiquetas, entonces cuando   compilado este programa como C ++, el   El compilador generaría:

typedef struct status status;
     

Lamentablemente, este nombre de tipo sería   conflicto con el nombre de la función, y   El programa no compilaría. Eso es   por qué C ++ no puede simplemente generar una   typedef para cada etiqueta.

     

En C ++, las etiquetas actúan como typedef   nombres, excepto que un programa puede   declarar un objeto, función o   enumerador con el mismo nombre y el   mismo alcance que una etiqueta. En ese caso, el   objeto, función o nombre del enumerador   oculta el nombre de la etiqueta. El programa puede   referirse a la etiqueta solo usando   la palabra clave clase, estructura, unión o   enumerar (según corresponda) delante de la   nombre de la etiqueta Un nombre de tipo que consiste en   una de estas palabras clave seguidas de una   La etiqueta es un especificador de tipo elaborado.   Por ejemplo, estado de estructura y enumeración   mes son elaborados especificadores de tipo

     

Por lo tanto, un programa en C que contiene ambos:

p = foo();
     

se comporta igual cuando se compila como C ++.   El nombre del estado solo se refiere a la   función. El programa puede referirse a la   escriba solo usando el   estructura-especificador de tipo elaborado   estado.

     

Entonces, ¿cómo hace esto para que los errores se deslicen?   en programas? Considere el programa en    Listado 1 . Este programa define una   clase foo con un constructor por defecto,   y un operador de conversión que   convierte un objeto foo a char const *.   La expresion

cout << p << '\n';
     

en main debería construir un objeto foo   y aplicar el operador de conversión. los   declaración de salida subsiguiente

p = class foo();
     

debería mostrar la clase foo, pero   no lo hace Muestra la función foo.

     

Este sorprendente resultado se produce porque   el programa incluye cabecera lib.h   se muestra en el Listado 2 . Este encabezado   define una función también llamada foo. los   nombre de la función foo oculta el nombre de la clase   foo, así que la referencia a foo en main   se refiere a la función, no a la clase.   principal puede referirse a la clase solo por   utilizando un especificador de tipo elaborado, como   in

typedef class foo foo;
     

La forma de evitar tal confusión.   a lo largo del programa es agregar el   siguiente typedef para el nombre de la clase   foo:

<*>      

inmediatamente antes o después de la clase   definición. Este typedef provoca una   conflicto entre el nombre de tipo foo y   el nombre de la función foo (de la   biblioteca) que activará una   error en tiempo de compilación.

     

No conozco a nadie que realmente escriba   estos typedefs como una cuestión de curso   Requiere mucha disciplina. Ya que   La incidencia de errores como el   uno en Listado 1 es probablemente bastante   Pequeños, muchos de ustedes nunca se ponen en conflicto con   este problema. Pero si un error en tu   el software puede causar lesiones corporales,   entonces deberías escribir el typedefs no   importa lo poco probable que sea el error.

     

No puedo imaginar por qué alguien lo haría   quiere ocultar un nombre de clase con una   función o nombre de objeto en el mismo   alcance como la clase. Las reglas de ocultacion   en C fueron un error, y deberían   no se han extendido a las clases en   C ++. De hecho, puede corregir el   error, pero requiere extra   Programando disciplina y esfuerzo que   no debería ser necesario.

Una diferencia más importante: typedef s no puede ser declarado. Así que para la opción typedef debe #include el archivo que contiene el typedef , que significa todo lo que #include es su .h también incluye ese archivo, ya sea que lo necesite directamente o no, y así sucesivamente. Definitivamente puede afectar sus tiempos de construcción en proyectos más grandes.

Sin el typedef , en algunos casos solo puede agregar una declaración de reenvío de struct Foo; en la parte superior de su archivo .h , y solo #include la definición de la estructura en su archivo .cpp .

Hay hay una diferencia, pero sutil. Míralo de esta manera: struct Foo introduce un nuevo tipo. El segundo crea un alias llamado Foo (y no un nuevo tipo) para un tipo struct sin nombre.

  

7.1.3 El especificador typedef

     

1 [...]

     

Un nombre declarado con el especificador typedef se convierte en un nombre typedef. Dentro del alcance de su declaración, un   typedef-name es sintácticamente equivalente a una palabra clave y nombra el tipo asociado con el identificador en   la forma descrita en la Cláusula 8. Un typedef-name es, por lo tanto, un sinónimo de otro tipo. Un typedef-name no introduce un nuevo tipo como lo hace una declaración de clase (9.1) o una declaración de enumeración.

     

8 Si la declaración typedef define una clase sin nombre (o enumeración), el primer nombre typedef declarado por la declaración   para ser ese tipo de clase (o tipo de enumeración) se usa para denotar el tipo de clase (o tipo de enumeración) para vinculación   sólo fines (3.5). [Ejemplo:

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

Por lo tanto, un typedef siempre se usa como marcador de posición / sinónimo para otro tipo.

No puede usar la declaración de reenvío con la estructura typedef.

La estructura en sí misma es de tipo anónimo, por lo que no tiene un nombre real para reenviar declarar.

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

Una declaración hacia adelante como esta no funcionará:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

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

Struct es crear un tipo de datos. El typedef es establecer un apodo para un tipo de datos.

Una diferencia importante entre 'typedef struct' y 'struct' en C ++ es que la inicialización de miembros en línea en 'typedef structs' no funcionará.

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

No hay diferencia en C ++, pero creo que en C le permitiría declarar instancias de la estructura Foo sin hacer explícitamente:

struct Foo bar;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top