41 Punteros a miembros de clases o estructuras

C++ permite declarar punteros a miembros de clases, estructuras y uniones. Aunque en el caso de las clases, los miembros deben ser públicos para que pueda accederse a ellos.

La sintaxis para la declaración de un puntero a un miembro es la siguiente:

<tipo> <clase|estructura|unión>::*<identificador>;

De este modo se declara un puntero "identificador" a un miembro de tipo "tipo" de la clase, estructura o unión especificada.

Ejemplos:

struct punto3D {
   int x;
   int y;
   int z;
};

class registro {
  public:
   registro();

   float v;
   float w;
};

int punto3D::*coordenada; // (1)
float registro::*valor;   // (2)

El primer ejemplo declara un puntero "coordenada" a un miembro de tipo int de la estructura "punto3D". El segundo declara un puntero "valor" a un miembro público de la clase "registro".

Asignación de valores a punteros a miembro

Una vez declarado un puntero, debemos asignarle la dirección de un miembro del tipo adecuado de la clase, estructura o unión. Podremos asignarle la dirección de cualquiera de los miembros del tipo adecuado. La sintaxis es:

<identificador> = &<clase|estructura|unión>::<campo>;

En el ejemplo anterior, serían válidas las siguientes asignaciones:

coordenada = &punto3D::x;
coordenada = &punto3D::y;
coordenada = &punto3D::z;
valor = &registro::v;
valor = &registro::w;

Operadores .* y ->*

Ahora bien, ya sabemos cómo declarar punteros a miembros, pero no cómo trabajar con ellos.

C++ dispone de dos operadores para trabajar con punteros a miembros: .* y ->*. A lo mejor los echabas de menos :-).

Se trata de dos variantes del mismo operador, uno para objetos y otro para punteros:

<objeto>.*<puntero>
<puntero_a_objeto>->*<puntero>

La primera forma se usa cuando tratamos de acceder a un miembro de un objeto.

La segunda cuando lo hacemos a través de un puntero a un objeto.

Veamos un ejemplo completo:

#include <iostream>
using namespace std;

class clase {
  public:
   clase(int a, int b) : x(a), y(b) {}

  public:
   int x;
   int y;
};

int main() {
   clase uno(6,10);
   clase *dos = new clase(88,99);
   int clase::*puntero;

   puntero = &clase::x;
   cout << uno.*puntero << endl;
   cout << dos->*puntero << endl;

   puntero = &clase::y;
   cout << uno.*puntero << endl;
   cout << dos->*puntero << endl;

   delete dos;
   return 0;
}

Ejecutar este código en OnlineGDB.

La utilidad práctica no es probable que se presente frecuentemente, y casi nunca con clases, ya que no es corriente declarar miembros públicos. Sin embargo nos ofrece algunas posibilidades interesantes a la hora de recorrer miembros concretos de arrays de estructuras, aplicando la misma función o expresión a cada uno.

También debemos recordar que es posible declarar punteros a funciones, y las funciones miembros de clases no son una excepción. En ese caso sí es corriente que existan funciones públicas.

#include <iostream>
using namespace std;

class clase {
  public:
   clase(int a, int b) : x(a), y(b) {}
   int funcion(int a) {
      if(0 == a) return x; else return y;
   }

  private:
   int x;
   int y;
};

int main() {
   clase uno(6,10);
   clase *dos = new clase(88,99);
   int (clase::*pfun)(int);

   pfun = &clase::funcion;

   cout << (uno.*pfun)(0) << endl;
   cout << (uno.*pfun)(1) << endl;
   cout << (dos->*pfun)(0) << endl;
   cout << (dos->*pfun)(1) << endl;

   delete dos;
   return 0;
}

Ejecutar este código en OnlineGDB.

Para ejecutar una función desde un puntero a miembro hay que usar los paréntesis, ya que el operador de llamada a función "()" tiene mayor prioridad que los operadores ".*" y "->*".