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 = &amp;clase::y;
   cout << uno.*puntero << endl;
   cout << dos->*puntero << endl;
   
   delete dos;
   return 0;
}

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;
}

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 "->*".

Comentarios de los usuarios (2)

Alberto
2015-02-03 21:47:39

Buenas,

Tengo una duda sobre punteros. Tengo declarada una subclase del tipo:

//Clase principal

class AppManager: public.....

{

private:

//Subclase

class OISListner : public OIS::Keylistener...

{

virtual void ......;

AppManager *mParent;

};

};

Quiero vincular éste (mParent) a otra clase de otro Header del mismo projecto.

Primero de todo, sabrian decirme que es mParent? Puntero? Objeto puntero?

Sabrian como lo podría tratar para solucionar el problema?

Muchas gracias

Alberto

Steven R. Davidson
2015-02-04 00:40:26

Hola Alberto,

Primeramente, 'mParent' es un puntero a un objeto de la clase 'AppManager'.

En cuanto a la duda que planteas, depende de a qué te refieres con "vincular". Si quieres apuntar al mismo objeto (de la clase 'AppManager'), entonces sólo copia este puntero a otro en la otra clase. Por ejemplo,

class Otra
{
private:
  AppManager *pam;

public:
  Otra( AppManager *ptr ) : pam(ptr)  {}
};

Cuando ya tengas los objetos necesarios, simplemente pasa 'mParent' a 'Otra' para que se copie a 'pam'.

Si en lugar de hacer esto quieres un vínculo mucho más estrecho, entonces puedes crear un puntero directamente al miembro, 'mParent'. Por ejemplo,

class Otra
{
  AppManager AppManager::OISListener::*pam;

  Otra()
  {
    pam = &AppManager::OISListener::mParent;
  }
};

Ahora podemos usar 'pam' como si fuere 'mParent' para cualquier objeto de la clase 'OISListener' (en 'AppManager'). Eso sí, necesitamos tener permiso para acceder a tales miembros.

Espero que esto te sirva.

Steven