¿Diferencia entre 'struct' y 'typedef struct' en C ++?
Pregunta
En C ++, ¿hay alguna diferencia entre:
struct Foo { ... };
y
typedef struct { ... } Foo;
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;