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]


4.2.1g Punteros a miembros-de-clases

Advertencia didáctica: la lectura de este capítulo exige un conocimiento previo de las clases ( 4.11). Además, puesto que los punteros-a-miembros de clases incluyen los punteros-a-métodos, que son un tipo de función, aconsejamos al lector novel que antes de entrar en este capítulo, repase con detenimiento el apartado relativo a punteros-a-función ( 4.2.4). Posteriormente, al volver aquí comprobará que los punteros-a-métodos son una variedad de aquellos.

§1 Sinopsis

Además de punteros genéricos a clases ( 4.2.1f), también es posible establecer punteros a miembros concretos (propiedades y métodos). Para establecer un cierto paralelismo, recordemos que la declaración genérica de puntero-a-tipoX adopta la forma tipoX*. Así, para declarar un puntero-a-entero se utiliza la expresión:

int* ptri;   // declara puntero-a-int

Posteriormente esta variable puede ser inicializada recibiendo un valor que es la dirección del objeto señalado. Por ejemplo:

int x = 5;
ptri = &x;


Del mismo modo, los punteros a miembro-tipoX-de-claseC, definen un tipo de objeto (constante o variable) que sirve para alojar las direcciones de miembros de instancias de la clase [1].

La declaración genérica de puntero a miembro tipoX-de-claseC adopta la forma tipoX C::*. A su vez, para iniciar un puntero a miembro es preciso aplicar el operador de referencia & ( 4.9.11) al nombre cualificado ( 4.1.11c) del miembro. En nuestro caso, la dirección cualificada de un miembro m de C es &C::m.

Nota: debe recordar que la asignación a miembros no está permitida en el cuerpo de la clase ( 4.11.2a) y que la asignación a punteros debe seguir las mismas reglas que para el resto de miembros ( 4.11.2d3). Recordar también que no es posible definir punteros a los constructores o destructores de clase, ya que no es posible obtener las direcciones de este tipo de miembros (4.11.2d).

§2 Ejemplos

A continuación se muestran algunos ejemplos de declaraciones de punteros a miembro-de-clase

int C::* ptr;

Puntero a miembro de C que es un int.

int* C::* ptr;

Puntero a miembro de C que es un puntero-a-int.

char* C::* ptr;

Puntero a miembro de C que es un puntero-a-char.

void (C::* fptr)(int);

Puntero a función miembro (método) de C que recibe un int y no devuelve nada.

int (C::* fptr) ();

Puntero a función miembro de C que no recibe argumentos y devuelve un int.

char const * (C::* fptr)();  Puntero a función miembro de C que no recibe argumentos y devuelve un puntero-a-char constante.

int (C::* fptr())();

Puntero a función miembro de C que no recibe argumentos y devuelve un puntero a función que no recibe argumentos y devuelve un int.

§3 Observaciones

Recordemos que los punteros desempeñan un papel fundamental en C++. Precisamente, algunas de las construcciones más elegantes y sofisticadas que pueden realizarse en él, los utilizan en sus diversas formas: punteros a objetos básicos (a tipos predefinidos en el lenguaje 2.2); punteros a funciones; punteros a clases, y a miembros de estas (propiedades y métodos). Sin embargo, como veremos a continuación, estos últimos (junto con los punteros-a-función) son quizás uno de los puntos de sintaxis C++ más compleja (por supuesto, los punteros-a-miembro incluyen punteros-a-función-miembro). A esto se suma el hecho de que los punteros a miembro requieren, sobre todo para el principiante, de un esfuerzo especial hasta lograr construir una imagen mental adecuada del mecanismo que los sustenta. El propio creador del lenguaje opina que son un punto oscuro: "... pointers to members (which I consider an obscure corner of C++)" [2].

Cuando se trata de punteros a miembros de clase, existen varios puntos a los que aconsejamos prestar especial atención:

  • Los punteros a miembros estáticos ( 4.11.7) tienen consideración distinta que los punteros a miembros no-estáticos. Esto se debe a que cada instancia de la clase contiene un subconjunto de los miembros no estáticos, mientras que solo existe una instancia de los miembros estáticos, que es común para todos los objetos de la clase, por lo que estos miembros se parecen más a objetos normales de un subespacio que a miembros de clase. Esta diferencia nos obliga a dividir el estudio de estos punteros en dos subapartados:

    • Punteros-a-miembros normales ( 4.2.1g1)

    • Punteros-a-miembros estáticos ( 4.2.1g2)

  • Las declaraciones utilizan una notación un tanto especial; Stroustrup hace referencia a ellas señalando "the lack of readability of the C declarator sintax", por lo que es muy frecuente la utilización de typedef ( 3.2.1a) en ellas.

    A lo largo de los ejemplos anteriores habrá observado que la notación utilizada para la declaración de punteros a propiedades es un tanto particular, pues se aparta de la que venimos utilizando. En cambio, la notación de punteros a métodos es similar a la utilizada para punteros a funciones normales ( 4.2.4), aunque con el añadido de utilizar el operador de acceso a ámbito :: ( 4.9.19).

  • La asignación se realiza de forma genérica (sobre miembros de clase), no sobre objetos concretos (miembros de instancia) como ocurría en el caso de punteros a clases. Aunque lógicamente la utilización de estos punteros se realiza sobre instancias concretas de la clase (ver ejemplo 4.2.1.g1).

  • La notación utilizada para la invocación de los valores señalados por estos punteros es la que cabría esperar. Como de costumbre, hay que aplicar la indirección al puntero *mptr, aunque además hay que aplicar el selector directo (.) o indirecto (->) para determinar a qué instancia concreta pertenece el miembro.

    Así pues, si mptr es un puntero-a-miembro de un objeto obj, y optr es un puntero-al-objeto, el miembro señalado por mptr viene determinado por las expresiones: obj.*mptr y optr->*mptr. Es decir, el operador de indirección * ( 4.9.11) sigue al selector directo . ( 4.9.16) o indirecto -> ( 4.9.16) de miembro según el caso. Ver ejemplos en las páginas siguientes.

  • Recordar que el puntero-a-void no puede ser utilizado para señalar a un miembro ( 4.2.1d)

§4 Sobre el rendimiento

Como consecuencia del mecanismo relativamente complicado que debe utilizarse, la utilización de funciones-miembro a través de punteros implica una gran penalización en términos del rendimiento global de la aplicación. En especial si estos accesos se realizan en bucles muy repetitivos y/o se trata de funciones virtuales ( 4.11.8a), por lo que deberían evitarse en la medida de lo posible.

Temas relacionados:
  • Punteros a clases implícitas ( 4.12.2).

  • La indirección de punteros a clases y a miembros ( 4.9.11)

§5 Bibliografía
  • Christopher Skelly "Powerful Pointers To Member Functions" C/C++ Users Journal Octubre 1994

  Inicio.


[1]  En este punto conviene no olvidar que, aunque coloquialmente nos referimos a ellos como "punteros a miembros de clase", en realidad queremos significar "punteros a miembros de objetos de clase". Como veremos más adelante, la primera expresión solo tiene sentido cuando nos referimos a punteros a miembros estáticos ( 4.11.7), ya que este tipo de miembros si pertenecen a la "clase" (son comunes para todas las instancias).

[2] Correspondencia del autor.