Pregunta ¿Diferencia entre 'struct' y 'typedef struct' en C ++?


En C ++, ¿hay alguna diferencia entre:

struct Foo { ... };

y

typedef struct { ... } Foo;

675
2018-03-04 20:41


origen


Respuestas:


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

El estándar de lenguaje C (C89 §3.1.2.3, C99 §6.2.3y C11 §6.2.3) ordena espacios de nombres separados para diferentes categorías de identificadores, incluidos identificadores de etiquetas (para struct/union/enum) y identificadores ordinarios (para 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 quiera referirse a un Foo, siempre tendrías que llamarlo 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) ambos se refieren a la misma cosa, y usted puede declarar libremente objetos de tipo Foo sin el struct palabra clave.


El constructo:

typedef struct Foo { ... } Foo;

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


Finalmente,

typedef struct { ... } Foo;

declara una estructura anónima y crea un typedef para ello. Por lo tanto, con este constructo, no tiene un nombre en el espacio de nombre de etiqueta, solo un nombre en el espacio de nombres typedef. Esto significa que tampoco puede ser declarado adelante. Si desea hacer una declaración directa, debe darle un nombre en el espacio de nombre de la etiqueta.


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


1018
2018-03-04 20:45



En este artículo de DDJ, Dan Saks explica un área pequeña donde los errores pueden aparecer si no escribes tus estructuras (¡y las 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 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 un programa C   declara una función y una estructura   estado nombrado:

int status(); struct status;

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

Si C ++ generó automáticamente   typedefs para etiquetas, luego cuando   compilado este programa como C ++, el   compilador generaría:

typedef struct status status;

Desafortunadamente, este tipo de nombre   conflicto con el nombre de la función, y   el programa no compilaría Eso es   por qué C ++ no puede simplemente generar un   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   consulte el nombre de la etiqueta solo mediante el uso   la palabra clave class, struct, union o   enum (según corresponda) enfrente de la   nombre de la etiqueta Un nombre tipo que consiste en   una de estas palabras clave seguida de una   tag es un especificador de tipo elaborado.   Por ejemplo, struct status y enum   mes son elaborados-tipo-especificadores.

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

int status(); struct status;

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

Entonces, ¿cómo esto permite que los insectos se arrastran?   en los programas? Considere el programa en    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 principal debería construir un objeto foo   y aplicar el operador de conversión. los   declaración de salida subsecuente

cout << p << '\n';

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

Este sorprendente resultado se produce porque   el programa incluye encabezado lib.h   se muestra en la 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, entonces la referencia a foo en main   se refiere a la función, no a la clase.   principal puede referirse a la clase solo por   usando un especificador de tipo elaborado, como   en

p = class foo();

La forma de evitar esa confusión   durante todo el programa es agregar el   siguiente typedef para el nombre de clase   foo:

typedef class foo foo;

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

No conozco a nadie que realmente escriba   estos typedefs como una rutina.   Requiere mucha disciplina. Ya que   la incidencia de errores como el   uno en Listado 1 es probablemente bastante   pequeño, muchos nunca se topan con   este problema. Pero si un error en su   el software puede causar lesiones corporales,   entonces deberías escribir el typedefs no   importa cuán improbable sea el error.

No puedo imaginar por qué alguien alguna vez   querer ocultar un nombre de clase con un   función o nombre del objeto en el mismo   alcance como la clase. Las reglas de ocultamiento   en C fueron un error, y deberían   no se ha extendido a las clases en   C ++. De hecho, puedes corregir el   error, pero requiere un extra   programación disciplina y esfuerzo que   no debería ser necesario.


203
2018-03-04 21:19



Una diferencia más importante: typedefs no puede ser declarado adelante. Entonces para el typedef opción que debe #include el archivo que contiene el typedef, significando todo lo que #includes tu .h también incluye ese archivo si lo necesita 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, puede agregar una declaración de struct Foo; en la parte superior de tu .h archivo, y solo #include la definición de estructura en su .cpp archivo.


57
2018-03-04 20:54



Ahí es 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 nombre sin nombre struct tipo.

7.1.3 El especificador typedef

1 [...]

Un nombre declarado con el especificador typedef se convierte en un typedef-name. 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   el modo descrito en la cláusula 8. Un typedef-name es, por lo tanto, un sinónimo de otro tipo. Un tipodef-nombre no introduce un nuevo tipo la forma en que 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 enum), el primer tipodef-name declarado por la declaración   ser ese tipo de clase (o tipo de enumeración) se usa para indicar el tipo de clase (o tipo de enumeración) para el enlace   fines únicamente (3.5). [Ejemplo:

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

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


28
2018-03-04 20:49



No puede usar la declaración forward con la estructura typedef.

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

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

Una declaración avanzada como esta no funcionará:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

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

10
2017-08-05 09:18



Una diferencia importante entre una 'typedef struct' y una '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; };

0
2018-04-11 13:26



Struct es crear un tipo de datos. Typedef es establecer un apodo para un tipo de datos.


-1
2018-04-08 03:06