Ya sabemos que los miembros privados de una clase no son accesibles para funciones y clases exteriores a dicha clase.
Esto es un concepto de POO, el encapsulamiento hace que cada objeto se comporte de un modo autónomo y que lo que pase en su interior sea invisible para el resto de objetos. Cada objeto sólo responde a ciertos mensajes y proporciona determinadas salidas.
Pero, en ciertas ocasiones, querremos tener acceso a determinados miembros privados de un objeto de una clase desde otros objetos de clases diferentes. C++ proporciona un mecanismo para sortear el sistema de protección. En otros capítulos veremos la utilidad de esta técnica, de momento sólo explicaremos en qué consiste.
El modificador "friend" puede aplicarse a clases o funciones para inhibir el sistema de protección.
Las relaciones de "amistad" entre clases son parecidas a las amistades entre personas:
El caso más sencillo es el de una relación de amistad con una función externa.
Veamos un ejemplo muy sencillo:
#include <iostream> using namespace std; class A { public: A(int i=0) : a(i) {} void Ver() { cout << a << endl; } private: int a; friend void Ver(A); // "Ver" es amiga de la clase A }; void Ver(A Xa) { // La función Ver puede acceder a miembros privados // de la clase A, ya que ha sido declarada "amiga" de A cout << Xa.a << endl; } int main() { A Na(10); Ver(Na); // Ver el valor de Na.a Na.Ver(); // Equivalente a la anterior cin.get(); return 0; }
Como puedes ver, la función "Ver", que no pertenece a la clase A puede acceder al miembro privado de A y visualizarlo. Incluso podría modificarlo.
No parece que sea muy útil, ¿verdad?. Bueno, seguro que en alguna ocasión tiene aplicaciones prácticas.
El siguiente caso es más común, se trata de cuando la función amiga forma parte de otra clase. El proceso es más complejo. Veamos otro ejemplo:
#include <iostream> using namespace std; class A; // Declaración previa (forward) class B { public: B(int i=0) : b(i) {} void Ver() { cout << b << endl; } bool EsMayor(A Xa); // Compara b con a private: int b; }; class A { public: A(int i=0) : a(i) {} void Ver() { cout << a << endl; } private: // Función amiga tiene acceso // a miembros privados de la clase A friend bool B::EsMayor(A Xa); int a; }; bool B::EsMayor(A Xa) { return b > Xa.a; } int main() { A Na(10); B Nb(12); Na.Ver(); Nb.Ver(); if(Nb.EsMayor(Na)) cout << "Nb es mayor que Na" << endl; else cout << "Nb no es mayor que Na" << endl; cin.get(); return 0; }
Puedes comprobar lo que pasa si eliminas la línea donde se declara "EsMayor" como amiga de A.
Es necesario hacer una declaración previa de la clase A (forward) para que pueda referenciarse desde la clase B.
Veremos que estas "amistades" son útiles cuando sobrecarguemos algunos operadores.
El caso más común de amistad se aplica a clases completas. Lo que sigue es un ejemplo de implementación de una lista dinámica mediante el uso de dos clases "amigas".
#include <iostream> using namespace std; /* Clase para elemento de lista enlazada */ class Elemento { public: Elemento(int t); /* Constructor */ int Tipo() { return tipo;} /* Obtener tipo */ private: /* Datos: */ int tipo; /* Tipo */ Elemento *sig; /* Siguiente elemento */ friend class Lista; /* Amistad con lista */ }; /* Clase para lista enlazada de números*/ class Lista { public: Lista() : Cabeza(NULL) {} /* Constructor */ /* Lista vacía */ ~Lista() { LiberarLista(); } /* Destructor */ void Nuevo(int tipo); /* Insertar figura */ Elemento *Primero() /* Obtener primer elemento */ { return Cabeza; } /* Obtener el siguiente elemento a p */ Elemento *Siguiente(Elemento *p) { if(p) return p->sig; else return p;}; /* Si p no es NULL */ /* Averiguar si la lista está vacía */ bool EstaVacio() { return Cabeza == NULL;} private: Elemento *Cabeza; /* Puntero al primer elemento */ void LiberarLista(); /* Función privada para borrar lista */ }; /* Constructor */ Elemento::Elemento(int t) : tipo(t), sig(NULL) {} /* Asignar datos desde lista de parámetros */ /* Añadir nuevo elemento al principio de la lista */ void Lista::Nuevo(int tipo) { Elemento *p; p = new Elemento(tipo); /* Nuevo elemento */ p->sig = Cabeza; Cabeza = p; } /* Borra todos los elementos de la lista */ void Lista::LiberarLista() { Elemento *p; while(Cabeza) { p = Cabeza; Cabeza = p->sig; delete p; } } int main() { Lista miLista; Elemento *e; // Insertamos varios valores en la lista miLista.Nuevo(4); miLista.Nuevo(2); miLista.Nuevo(1); // Y los mostramos en pantalla: e = miLista.Primero(); while(e) { cout << e->Tipo() << " ,"; e = miLista.Siguiente(e); } cout << endl; cin.get(); return 0; }
La clase Lista puede acceder a todos los miembros de Elemento, sean o no públicos, pero desde la función "main" sólo podemos acceder a los miembros públicos de nuestro elemento.
friend.
© Marzo de 2001 Salvador Pozo, salvador@conclase.net