Saltar al contenido

¿Diferencia entre ‘struct’ y ‘typedef struct’ en C ++?

Solución:

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

El estándar del 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, incluyendo identificadores de etiquetas (por struct/union/enum) y identificadores ordinarios (por typedef y otros identificadores).

Si tan solo dijeras:

struct Foo { ... };
Foo x;

obtendría un error del compilador, porque Foo solo se define en el espacio de nombres de la etiqueta.

Tendrías que declararlo como:

struct Foo x;

En cualquier momento que desee hacer referencia a un Foo, siempre tendrías que llamarlo struct Foo. Esto se vuelve molesto rápidamente, por lo que puede agregar un typedef:

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

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


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 ello. 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 ++, todos struct/union/enum/class las declaraciones actúan como si fueran implícitamente typedef‘ed, siempre que el nombre no esté oculto por otra declaración con el mismo nombre. Consulte la respuesta de Michael Burr para conocer todos los detalles.

En este artículo de DDJ, Dan Saks explica un área pequeña donde los errores pueden infiltrarse si no escribe def sus 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, suponga 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 la estructura se refiere al tipo.

Si C ++ generara automáticamente typedefs para etiquetas, entonces cuando compilaras 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 como nombres typedef, 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 nombre del objeto, función o enumerador oculta el nombre de la etiqueta. El programa puede hacer referencia al nombre de la etiqueta solo mediante el uso de la palabra clave class, struct, union o enum (según corresponda) delante del nombre de la etiqueta. Un nombre de tipo que consta de una de estas palabras clave seguida de una etiqueta es un especificador de tipo elaborado. Por ejemplo, el estado de la estructura y el mes de la enumeración son especificadores de tipo elaborado.

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

int status(); struct status;

se comporta igual cuando se compila como C ++. El estado del nombre solo se refiere a la función. El programa puede hacer referencia al tipo solo mediante el estado de estructura del especificador de tipo elaborado.

Entonces, ¿cómo permite esto que los errores se introduzcan en los programas? Considere el programa del Listado 1. Este programa define una clase foo con un constructor predeterminado y un operador de conversión que convierte un objeto foo en 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';

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

Este resultado sorprendente ocurre 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 en 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 en 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 definición de clase. Este typedef provoca un conflicto entre el nombre del tipo foo y el nombre de la función foo (de la biblioteca) que desencadenará un error en tiempo de compilación.

No conozco a nadie que realmente escriba estas definiciones de tipo como algo natural. Requiere mucha disciplina. Dado que la incidencia de errores como el del Listado 1 es probablemente bastante pequeña, es posible que nunca se enfrente a este problema. Pero si un error en su software puede causar lesiones corporales, entonces debe escribir typedefs 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.

Una diferencia más importante: typedefs no se puede declarar hacia adelante. Entonces para el typedef opción que debes #include el archivo que contiene el typedef, es decir todo lo que #includees 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, simplemente puede agregar una declaración de reenvío de struct Foo; en la parte superior de tu .h archivo, y solo #include la definición de estructura en tu .cpp expediente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *