Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


3.2.1a  typedef

§1 Sinopsis

La palabra reservada typedef se utiliza para asignar un alias (otro nombre) a un tipo. No crea ningún nuevo tipo, solo define un nuevo identificador (type-id 2.2) para un tipo que ya tiene su propio identificador (el identificador puede ser un nombre o una expresión compleja que contiene al nombre). Es importante recalcar que el nuevo nombre es un añadido, y no sustituye al identificador original. Ambos identificadores pueden ser intercambiados libremente en cualquier expresión.

Formalmente typedef es un especificador de identificador (nombre). Su introducción en el lenguaje se debe a que, como reconoce su propio creador [1], la sintaxis de declaraciones C++ es innecesariamente dura de leer y escribir. Esto se debe a la herencia del C y a que la notación no es lineal, sino que mimetiza la sintaxis de las expresiones que está basada en precedencias. En este sentido, la utilización de typedef permite paliar en parte el problema, mejora la legibilidad y ayuda a la documentación y mantenimiento del programa, ya que permite sustituir identificadores de tipo complejos por expresiones más sencillas.

§2 Sintaxis:

typedef <tipo> <alias>;

Asigna un nombre <alias> con el tipo de dato de <tipo>.

Nota: para distinguir un identificador <alias> introducido con un typedef de un nombre de tipo normal <tipo>, al primero se le denomina nombre-typedef ("typedef-name") o alias-typedef ("typedef-alias").

Los typedef pueden ser usados con tipos simples o abstractos, pero no con nombres de funciones.

§3 Ejemplos:

typedef unsigned char BYTE;

en lo sucesivo, cualquier referencia a BYTE (es una tradición de C/C++ utilizar los alias-typedef en mayúsculas) equivale a colocar en su lugar unsigned char, incluso para crear nuevos tipos unsigned char:

BYTE z, y     // equivale a: unsigned char z, y

...

typedef const float KF;

typedef const float* KF_PTR;

KF pi = 3.14;

KF_PTR ppi = &pi;

typedef long clock_t;    // no sería muy C++, mejor CLOCK_T

clock_t slice = clock();


§3.1 En realidad el nuevo identificador introducido por typedef se comporta dentro de su ámbito como una nueva palabra-clave con la que pueden declararse nuevos tipos:

typedef int* Pint;

...

Pint p1, p2;    // Ok. p1 y p2 son punteros-a-int

 También pueden encadenarse, de forma que pueden definirse nuevos alias en función de otros definidos previamente. Por ejemplo:

typedef  char CHAR;

typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;


§3.2 Como en otras declaraciones, es posible definir una serie de alias-typedef mediante expresiones unidas por comas. Por ejemplo:

typedef const char * LPCCH, * PCCH, * LPCSTR, * PCSTR ;

...

LPCCH ptr1 = "Hola América";

PCSTR ptr2 = "Hola América";

const char* ptr3 = "Hola América";

Los punteros ptr1; ptr2 y ptr3 son equivalentes.

Otros ejemplos (tomados de definiciones reales de MS VC++)

typedef long INT_PTR, *PINT_PTR;

typedef unsigned short UHALF_PTR, *PUHALF_PTR;

typedef short HALF_PTR, *PHALF_PTR;


§3.3
Otros ejemplos de typedef utilizados frecuentemente en la programación Windows ( Ejemplos):

§4 Asignaciones complejas

Es útil utilizar typedef para asignaciones complejas; en particular en la declaración y definición de punteros a funciones. Por ejemplo:

typedef long (*(*(*FPTR)())[5])();          // L.1

FPTR an;                                    // L.2

La sentencia L.1 define un identificador: FPTR como un puntero a función que no recibe argumentos y devuelve un puntero a array de 5 punteros a función, que no reciben ningún parámetro y devuelven long. Después, L.2 declara que an es un elemento del tipo indicado.

typedef void (new * new_handler)();           // L.3
new_handler set_new_handler(new_handler);     // L.4

L:3 define new_handler como el identificador de un puntero a función que no recibe argumentos y devuelve void [2].  Después L.4 declara set_new_handler como el nombre de una función que devuelve el tipo new_handler recién definido y acepta un único argumento de este mismo tipo.

typedef void (X::*PMF)(int);                    // L.5

PMF pf = &X::func;                              // L.6  

L.5 define el identificador PMF como puntero a función-miembro de la clase X que recibe un int y no devuelve nada. L.6 declara pf como tipo PMF (puntero a función...) que apunta al método func de X.


§4.1 Cuando se trata de expresiones muy complejas, puede ser útil proceder por fases, aprovechando la propiedad ya enunciada (§3.1 ) de que pueden definirse nuevos alias en función de otros definidos previamente . Por ejemplo, queremos definir un puntero p a matriz de 5 punteros a función que toman un entero como argumento y devuelven un puntero a carácter. En este caso, puede ser conveniente empezar por las funciones:

char* foo(int);       // foo es una función aceptando int y devolviendo char

typedef char* F(int); // F es un alias de función aceptando int y devolviendo...

a continuación definimos la matriz:

F* m[5];              // m es una matriz de 5 punteros a F

typedef F* M[5];      // M es el alias de una matriz de 5 punteros a F

Finalmente escribimos la declaración del tipo solicitado:

M* p;

Cualquier manipulación posterior del tipo mencionado resulta ahora mucho más sencilla. Por ejemplo, la declaración:

int* foo(M*);

corresponde a una función aceptando un puntero a matriz... que devuelve un puntero a int.


§4.2  En ocasiones, la simplificación mencionada el en párrafo anterior, permite solventar algún que otro problema con la compilación de expresiones complejas.  Por ejemplo, un miembro de cierta clase tiene la siguiente declaración: 

class MYClass {

   ...

   MyNAMESPACE::Garray<MyNAMESPACE::MyString> arrQuer;

   ...

}

Como sugiere su nombre, Garray es una clase genérica, destinada a almacenar matrices de objetos de cualquier tipo, está definida en el espacio de nombres MyNAMESPACE. En este caso, el objeto arrQuer es una matriz de objetos tipo MyString, que es una clase para manejar cadenas de caracteres -similar a la conocida string de la librería estándar de plantillas STL- también definida en MyNAMESPACE.  En resumen, arrQuer es una matriz de cadenas de caracteres.

Ocurre que deseo incluir una invocación explícita para la destrucción del objeto arrQuer en el destructor de MYClass,  Algo como:

class MYClass {

   ...

   MyNAMESPACE::Garray<MyNAMESPACE::MyString> arrQuer;

   ...

   ~MYClass() {

      arrQuer.~MyNAMESPACE::Garray<MyNAMESPACE::MyString>();     // Compiler error

   }

}

 Sin embargo, el compilador [3] muestra un mensaje de error porque no es capaz de interpretar correctamente la sentencia señalada.  En este caso, la utilización de un typedef resuelve el problema y el compilador construye sin dificultad la aplicación.

class MYClass {

   ...

   typedef MyNAMESPACE::Garray<MyNAMESPACE::MyString> ZSTR;

   ZSTR arrQuer;

   ...

   ~MYClass() {

      arrQuer.~ZSTR();     // Compilación Ok.

   }

}


&5 También es posible utilizar typedef al mismo tiempo que se declara una estructura u otro tipo de clase.  Ejemplo:

typedef {
  double re, im;
} COMPLEX;

...

COMPLEX c, *ptrc, Arrc[10];


La anterior corresponde a la definición de una estructura anónima que puede ser utilizada a través de su typedef. Por supuesto, aunque no es usual necesitar el nombre y el alias simultáneamente, también se puede poner nombre a la estructura al mismo tiempo que se declara el typedef. Por ejemplo:

typedef struct C1 {
  double re, im;
} COMPLEX;

...

C1 c, *ptrc;

COMPLEX Arrc[10];

  Otro ejemplo tomado de una definición real: ( 4.5.5c)


&6  Otro uso de typedef puede consistir en localizar determinadas referencias concretas en un solo punto (donde se declara el typedef), de forma que los posibles cambios posteriores solo requieren cambiar una línea de código  ( en mi opinión, quizás sea esta la razón más importante para la utilización de este especificador). Por ejemplo, supongamos que necesitamos una variable de 32 bits y estamos utilizando C++Builder, en el que int tiene justamente 32 bits ( 2.2.4); a pesar de ello utilizamos la expresión:

typedef int INT32;

en lo sucesivo, para todas las referencias utilizamos INT32 en vez de int. Por ejemplo:

INT32 x, y, z;       

Si tuviésemos que portar el código a una máquina o a un compilador en el que int fuese menor y, por ejemplo, los 32 bits exigieran un long int, solo tendríamos que cambiar la línea de código del typedef:

typedef long INT32;  // Todas las demás referencias se mantienen

...

INT32 x, y, z;       // Ok.


§7  No está permitido usar typedef con clases de declaración adelantada ( 4.11.4). Por ejemplo sería incorrecto:

typedef struct COMPLEX;   // Ilegal!!

Tampoco está permitido utilizarlo en la definición de funciones o utilizar dos veces el mismo identificador en el mismo espacio de nombres:

typedef long INT32;

...

typedef float INT32;      // Error!!


Cuando el alias-typedef se refiere a una clase, se constituye en un nombre de clase. Este alias no puede ser utilizado después de los prefijos class, struct o union. Tampoco puede ser utilizado con los nombres de constructores o destructores dentro de la clase. Ejemplo:

typedef struct S {

  ...

   S();           // constructor

   ~S();          // destructor

} STR;

...

S o1 = STR();     // Ok.

STR o2 = STR();   // Ok.

struct STR* sp;   // Error!!

Observe que:

typedef struct {

  ...

   S();          // Error!!

   ~S();         // Error!!

} STR;

...

S() y ~S() serían tratadas como funciones normales, no como constructor y destructor de la estructura.


§8 Cuando se quiere utilizar un alias para el identificador de un espacio de nombres, el mecanismo es distinto; no es necesario utilizar typedef.  Ver: Alias de subespacios ( 4.1.11a).

  Inicio.


[1] Stroustrup & Ellis: ACRM §8.2

[2] Se trata precisamente de la descripción del manejador de errores del operador new, contenido en <new.h> ( 4.9.20d).

[3]  En este caso se ha utilizado el compilador Microsoft Visual Studio 2008 Version 9.0.21022.8