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 = ®istro::v; valor = ®istro::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 "->*".