Curso de C++ v2.0
Consultas, lista de correo 'C++ Con Clase' 'C++ Con Clase' página de entrada Librerías estándar C Tabla de contenido Contactar con Webmaster
*Introducción
*1 Toma de contacto
*2 Variables I
*3 Funciones I: Declaración y definición
*4 Operadores I
*5 Sentencias
*6 Declaración de variables
*7 Normas para la notación
*8 Cadenas de caracteres
*9 Conversión de tipos
*10 Variables II: Arrays
*11 Variables III: Estructuras
*12 Variables IV: Punteros 1
*13 Operadores II: Más operadores
*14 Operadores III: Precedencia
*15 Funciones II: Parámetros por valor y referencia
*16 Variables V: Uniones
*17 Variables VI: Punteros 2
*18 Operadores IV: De bits y condicional
*19 Definición de tipos
*20 Funciones III
*21 Funciones IV: Sobrecarga
*22 Operadores V: Sobrecarga
*23 El preprocesador
*24 Funciones V: Recursividad
*25 Variables VII: Modificadores
*26 Espacios con nombre
*27 Clases I: Definiciones
*28 Declaración de clases
*29 Constructores
*30 Destructores
*31 El puntero this
*32 Sistema de protección
 . Declaraciones friend
 . Funciones amigas externas
 . Funciones amigas en otras clases
 . Clases amigas
*33 Modificadores para miembros
*34 Más sobre funciones
*35 Operadores sobrecargados
*36 Herencia
*37 Funciones virtuales
*38 Derivación múltiple
*39 Trabajar con ficheros
*40 Plantillas
*41 Punteros a miembros
*42 Castings
*43 Excepciones
*Ejemplos capítulos 1 a 6
*Ejemplos capítulos 8 y 9
*A Palabras reservadas C/C++
*B Trigrafos y símbolos alternativos
*C Librerías estándar
*D Streams
<< < > >>

32 Sistema de protección

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.

Declaraciones friend  

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:

  • La amistad no puede transferirse, si A es amigo de B, y B es amigo de C, esto no implica que A sea amigo de C. (La famosa frase: "los amigos de mis amigos son mis amigos", es falsa en C++, y probablemente también en la vida real).
  • La amistad no puede heredarse. Si A es amigo de B, y C es una clase derivada de B, A no es amigo de C. (Los hijos de mis amigos, no tienen por qué ser amigos míos. De nuevo, el símil es casi perfecto).
  • La amistad no es simétrica. Si A es amigo de B, B no tiene por qué ser amigo de A. (En la vida real, una situación como esta hará peligrar la amistad de A con B, pero de nuevo me temo que en realidad se trata de una situación muy frecuente, normalmente A no sabe que B no se considera su amigo).

Funciones amigas externas  

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.

Funciones amigas en otras clases

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.

Clases amigas.  

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.

Palabras reservadas usadas en este capítulo

friend.

<< < > >>
Free Web Hosting