c++ - reservada - typedef struct array
¿Diferencia entre ''struct'' y ''typedef struct'' en C++? (8)
En este artículo de DDJ , Dan Saks explica un área pequeña donde los errores pueden aparecer si no escribes tus estructuras (y clases):
Si lo desea, puede imaginar que C ++ genera un typedef para cada nombre de etiqueta, como
typedef class string string;
Desafortunadamente, esto no es del todo exacto. Ojalá fuera así de simple, pero no lo es. C ++ no puede generar tales typedefs para estructuras, uniones o enumeraciones sin introducir incompatibilidades con C.
Por ejemplo, supongamos que un programa en C declara tanto una función como una estructura llamada status:
int status(); struct status;
Nuevamente, esto puede ser una mala práctica, pero es C. En este programa, el estado (por sí mismo) se refiere a la función; El estado de estructura se refiere al tipo.
Si C ++ generó automáticamente typedefs para las etiquetas, entonces cuando compilaste este programa como C ++, el compilador generaría:
typedef struct status status;
Desafortunadamente, este nombre de tipo entraría en conflicto con el nombre de la función y el programa no se compilaría. Es por eso que C ++ no puede simplemente generar un typedef para cada etiqueta.
En C ++, las etiquetas actúan igual que los nombres typedef, excepto que un programa puede declarar un objeto, función o enumerador con el mismo nombre y el mismo ámbito que una etiqueta. En ese caso, el nombre del objeto, la función o el enumerador oculta el nombre de la etiqueta. El programa puede referirse al nombre de la etiqueta solo mediante la clase de palabra clave, estructura, unión o enumeración (según corresponda) delante del nombre de la etiqueta. Un nombre de tipo que consiste en una de estas palabras clave seguidas de una etiqueta es un especificador de tipo elaborado. Por ejemplo, el estado de estructura y el mes de enumeración son especificaciones de tipo elaborado.
Así, un programa en C que contiene ambos:
int status(); struct status;
Se comporta igual cuando se compila como C ++. El nombre del estado solo se refiere a la función. El programa puede referirse al tipo solo utilizando el estado de estructura de especificador de tipo elaborado.
Entonces, ¿cómo esto permite que los errores se introduzcan en los programas? Considere el programa en el Listado 1 . Este programa define una clase foo con un constructor predeterminado y un operador de conversión que convierte un objeto foo a char const *. La expresion
p = foo();
en main debería construir un objeto foo y aplicar el operador de conversión. La declaración de salida posterior
cout << p << ''/n'';
debe mostrar la clase foo, pero no lo hace. Muestra la función foo.
Este resultado sorprendente se produce porque el programa incluye el encabezado lib.h que se muestra en el Listado 2 . Este encabezado define una función también llamada foo. El nombre de la función foo oculta el nombre de la clase foo, por lo que la referencia a foo in main se refiere a la función, no a la clase. main puede referirse a la clase solo mediante el uso de un especificador de tipo elaborado, como en
p = class foo();
La forma de evitar tal confusión a lo largo del programa es agregar el siguiente typedef para el nombre de clase foo:
typedef class foo foo;
Inmediatamente antes o después de la definición de la clase. Este typedef causa un conflicto entre el nombre de tipo foo y el nombre de la función foo (de la biblioteca) que provocará un error en tiempo de compilación.
No conozco a nadie que realmente escriba estas definiciones de tipo como una cuestión de rutina. Requiere mucha disciplina. Debido a que la incidencia de errores como la del Listado 1 es probablemente muy pequeña, es probable que nunca se encuentren en conflicto con este problema. Pero si un error en su software puede causar lesiones corporales, debe escribir las definiciones de tipo sin importar cuán improbable sea el error.
No puedo imaginar por qué alguien querría ocultar un nombre de clase con una función o un nombre de objeto en el mismo ámbito que la clase. Las reglas de ocultación en C fueron un error, y no deberían haberse extendido a las clases en C ++. De hecho, puede corregir el error, pero requiere una disciplina de programación adicional y un esfuerzo que no debería ser necesario.
En C ++, ¿hay alguna diferencia entre:
struct Foo { ... };
y
typedef struct { ... } Foo;
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, incluidos los identificadores de etiqueta (para struct
/ union
/ enum
) y los identificadores ordinarios (para typedef
y otros identificadores).
Si acabas de decir:
struct Foo { ... };
Foo x;
obtendrías un error de compilador, porque Foo
solo está definido en el espacio de nombres de la etiqueta.
Tendrías que declararlo como:
struct Foo x;
Siempre que quieras referirte a un Foo
, siempre deberías llamarlo una struct Foo
. Esto se vuelve molesto rápidamente, así 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 libremente objetos de tipo Foo
sin la palabra clave struct
.
El constructo:
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 asignarle un nombre en el espacio de nombres de la etiqueta .
En C ++, todas class
declaraciones de struct
/ union
/ enum
/ class
actúan como si estuvieran implícitamente typedef
, siempre que el nombre no esté oculto por otra declaración con el mismo nombre. Vea la respuesta de Michael Burr para los detalles completos.
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 de 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, typedef-name es sintácticamente equivalente a una palabra clave y nombra el tipo asociado con el identificador de la manera 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 enum.
8 Si la declaración typedef define una clase sin nombre (o enumeración), el primer nombre typedef declarado por la declaración como ese tipo de clase (o tipo enum) se usa para denotar el tipo de clase (o tipo enum) solo para fines de vinculación ( 3.5). [Ejemplo:
typedef struct { } *ps, S; // S is the class name for linkage purposes
Por lo tanto, un typedef siempre se utiliza como un marcador de posición / sinónimo para otro tipo.
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;
No se puede utilizar la declaración hacia adelante con la estructura typedef.
La estructura en sí es un 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 un ''typedef struct'' y un ''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; };
Una diferencia más importante: typedef
s no puede ser declarado hacia adelante. Por lo tanto, para la opción typedef
debe #include
el archivo que contiene el typedef
, es decir, todo lo que #include
s 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 simplemente puede agregar una declaración de struct Foo;
de struct Foo;
en la parte superior de su archivo .h
, y solo #include
la definición de la estructura en su archivo .cpp
.