Operador de indirección: ejemplo
El programa muestra un esquema de la casuística de acceso a instancias de clase y a miembros utilizando el operador
de indirección * estándar, así como los operadores específicos
.* y ->* ofrecidos
por C++ para estos casos.
El ejemplo define una clase C que tiene tres propiedades y un método. Tanto la propiedad x como el método fm son referenciados por sendos punteros: uno de ellos es miembro de la propia clase; el otro es exterior a ella. El cuerpo de main se limita a instanciar un objeto c1 de la clase y un puntero pc al objeto. A continuación se muestran diversos ejemplos de la sintaxis posible para acceder a la propiedad x del objeto, e invocar al método fm a través de sus punteros.
#include <iostream>
using namespace std;
class C {
public:
int x;
void fm() {cout << "Funcion" << endl; }
int C::* mpint; // miembro
puntero-a-int
void (C::* mpfm)(); // miembro puntero-a-funcion miembro
C (int n = 0) { // constructor por defecto
x = n;
mpint = &C::x;
mpfm = &C::fm;
}
};
int (C::* pmint) = &C::x; // puntero-a-miembro-int
void (C::* pfm)() = &C::fm; // puntero-a-miembro-funcion
int main (void) { // ========================
C c1(10);
// M.1: Instancia objeto c1
C* pc = &c1;
// define puntero al objeto
// acceso al miembro x
cout << "1.a.- c1.x == " << c1.x
<< endl;
cout << "1.b.- c1.x == " << c1.*pmint << endl;
cout << "1.c.- c1.x == " << c1.*(c1.mpint) << endl;
cout << "1.c1- c1.x == " << c1.*c1.mpint << endl;
// Eq.
// sustitucion de c1 por su equivalente *pc
cout << "2.a.- c1.x == " << (*pc).x
<< endl;
cout << "2.b.- c1.x == " << (*pc).*pmint << endl;
cout << "2.c.- c1.x == " << (*pc).*((*pc).mpint) << endl;
cout << "2.c1- c1.x == " << (*pc).*(*pc).mpint << endl; // Eq.
// sustitucion de (*pc). por el
su quivalente pc->
cout << "3.a.- c1.x == " << pc->x
<< endl;
cout << "3.b.- c1.x == " << pc->*pmint
<< endl;
cout << "3.c.- c1.x == " << pc->*(pc->mpint) << endl;
cout << "3.c1- c1.x == " << pc->*pc->mpint << endl; // Eq.
// invocacion del metodo fm del objeto c1
cout << "4.a.- Invocar c1.fm() == "; c1.fm();
cout << "4.b.- Invocar c1.fm() == "; (c1.*pfm)();
cout << "4.c.- Invocar c1.fm() == "; (c1.*(c1.mpfm))();
cout << "4.c1- Invocar c1.fm() == "; (c1.*c1.mpfm)(); // Eq.
// sustitucion de c1 por su equivalente *pc
cout << "5.a.- Invocar c1.fm() == "; (*pc).fm();
cout << "5.b.- Invocar c1.fm() == "; ((*pc).*pfm)();
cout << "5.c.- Invocar c1.fm() == "; ((*pc).*((*pc).mpfm))();
cout << "5.c1- Invocar c1.fm() == "; ((*pc).*(*pc).mpfm)();
// sustitucion de (*pc). por el su quivalente pc->
cout << "6.a.- Invocar c1.fm() == "; pc->fm();
cout << "6.b.- Invocar c1.fm() == "; (pc->*pfm)();
cout << "6.c.- Invocar c1.fm() == "; (pc->*(pc->mpfm))();
cout << "6.c1- Invocar c1.fm() == "; (pc->*pc->mpfm)();
return 0;
}
Salidas: solo hay dos tipos (todas iguales):
x.y.- c1.x == 10
m.n.- Invocar c1.fm() == Funcion
Comentario
La precedencia de operadores ha permitido suprimir paréntesis en algunas sentencias. En estos casos se ha incluido la expresión simplificada equivalente debajo de la original con la indicación Eq (Equivalente).
Las sentencias se agrupan en seis bloques que forman dos grandes grupos: Los tres primeros muestran diversas formas de acceso a la propiedad x del objeto c1. Los tres últimos son formas de invocación del método fm. Dentro de cada grupo las sentencias con la misma letra guardan cierta relación entre si.
La salida 1.a del primer grupo, obtiene el valor del miembro x directamente, mediante la utilización del
selector directo de miembro .
( 4.9.16).
Las salidas 1.b y 1.c representan el acceso a x mediante los punteros externo e interno respectivamente (es
digna de atención la sintaxis utilizada en las formas n.c de acceso a miembros mediante punteros internos).
El grupo de salidas 2 se ha obtenido del anterior sustituyendo el designador del objeto (c1) por su valor equivalente, que se obtiene mediante indirección de su puntero ( *pc ).
El grupo 3 se obtiene del anterior mediante sustitución de la expresión
(*pc). por su sintaxis equivalente utilizando el selector indirecto
-> (
4.9.16d).
El proceso se repite de forma paralela con los grupos 4, 5 y 6, aunque en este caso, el acceso a fm sirve para invocar la ejecución de este método en la instancia c1.
Cuando el objeto c1 está disponible se utilizan las formas 1 y 4. Las restantes solo son útiles cuando el objeto c1 no es accesible directamente, sino a través de su puntero pc (por ejemplo cuando el objeto pasa como argumento a una función mediante un puntero). Las formas 2 y 5, aunque perfectamente válidas no son muy usuales. En su lugar se prefiere utilizar las formas equivalentes 3 y 6 (precisamente una de las razones para la introducción de este operador es, entre otras [1], simplificar la sintaxis).
[1] En realidad, el acceso a miembros está perfectamente atendido con la sintaxis existente, que aunque
farragosa, no lo es más que el resto de la sintaxis C++. Otra razón para la introducción de un único operador capaz de manejar una
expresión del tipo *pc. es permitir su sobrecarga
( 4.9.18e).