|
Tema
18 Clases:
Definiciones
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Teoría:
Clases
y funciones friend
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ya
se ha visto anteriormente que declarar como privadas las
variables miembro de una clase ofrece muchas ventajas.
De todos modos, en algunos casos, puede suceder que dos clases vayan
a trabajar conjuntamente con los mismos datos, y utilizar las funciones
públicas de acceso a esos datos no es la manera más
eficiente de hacerlo; conviene recordar que llamar a una función
tiene un coste y que es mucho más eficiente acceder directamente
a una variable. Ésta no es la única limitación
de las funciones miembro, ya que una función
sólo puede ser miembro de una única clase.
Además una función miembro convencional sólo
puede actuar directamente sobre un único objeto de la clase
(su argumento implícito, que es el objeto con el que ha sido
llamada por medio del operador punto (.) o flecha (->), como
por ejemplo en c1.Ingreso(10000)). Para que actúe
sobre un segundo o un tercer objeto -por ejemplo, para hacer transferencias-
hay que pasárselos como argumentos.
Se
puede concluir que a pesar de las grandes ventajas que tiene el
encapsulación, en muchas ocasiones es necesario
dotar a la programación orientada a objetos de una mayor
flexibilidad. Esto se consigue por medio de las funciones
friend. Una función friend de
una clase es una función que no pertenece a la clase,
pero que tiene permiso para acceder a sus variables
y funciones miembro privadas por medio de los operadores punto (.)
y flecha (->), sin tener que recurrir a las funciones miembro
públicas de la clase. Si una clase se declara friend
de otra, todas sus funciones miembro son friend
de esta segunda clase. El carácter de friend puede
restringirse a funciones concretas, que pueden ser miembro de alguna
clase o pueden ser funciones generales que no pertenecen a ninguna
clase..
Para
declarar una función o una clase como
friend de otra clase, es necesario hacerlo en la declaración
de la clase que debe autorizar el acceso a sus datos privados.
Esto se puede hacer de forma indiferente en la zona de los datos
o en la de los datos privados. Un ejemplo de declaración
de una clase friend podría ser el que sigue:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class
Cualquiera
{
friend
class Amiga;
private:
int secreto;
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Es
muy importante tener en cuenta que esta relación funciona
sólo en una dirección, es decir, las funciones miembro
de la clase Amiga pueden acceder a las variables privadas
de la clase Cualquiera, por ejemplo a la variable
entera secreto, pero esto no es cierto en sentido
inverso: las funciones miembro de la clase Cualquiera
no puede acceder a un dato privado de la clase Amiga.
Si
se quiere que varias clases tengan acceso mutuo a todas las variables
y funciones miembro privadas, cada una debe declararse como
friend en todas las demás, para conseguir una relación
recíproca.
Otro
aspecto que hay que mencionar es que al definir una clase como
friend no se está haciendo friend a
todas las clases que se deriven de ella (esto se entenderá
al llegar al capítulo de la herencia).
Hay
que tener en cuenta que para que una función que no es miembro
de una clase pueda recibir como argumentos explícitos objetos
de esa clase, debe ser declarada friend de esa clase.
Un ejemplo de una función friend es el que
sigue:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class
Cualquiera
{
friend
void fAmiga(Cualquiera);
private:
int secreto;
};
void
fAmiga(Cualquiera Una)
{
Una.secreto++;
// Modifica el valor de la variable privada
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Recuérdese
que una función puede ser declarada friend
de cuantas clases se quiera, pero sólo puede ser miembro
de una única clase. En algunos casos interesará que
no sea miembro de ninguna y que sea friend de una
o más clases.
Se
plantea un problema cuando dos clases deben ser declaradas
mutuamente friend la una de la otra. Considérense
por ejemplo las clases vector y matriz. Las declaraciones
podrían ser como se muestra a continuación:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
declaración de las clases vector y matriz (mutuamente friend)
class matriz; // declaración anticipada de la clase matriz
class
vector {
//
declaración de funciones miembro de la clase vector
...
friend matriz;
};
class
matriz {
//
declaración de funciones miembro de la clase matriz
...
friend vector;
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A
primera vista sorprende la segunda línea de lo que sería
el fichero de declaración de ambas clases. En esta línea
aparece la sentencia class matriz;. Esto es lo que
se llama una declaración anticipada, que es
necesaria por el motivo que se explica a continuación. Sin
declaración anticipada cuando la clase matriz aparece
declarada como friend en la clase vector,
el compilador aún no tiene noticia de dicha clase, cuya declaración
viene después. Al no saber lo que es matriz,
da un error. Si la clase matriz se declara antes que
la clase vector, el problema se repite, pues vector
se declara como friend en matriz.
La única solución es esa declaración
anticipada que advierte al compilador que matriz es
una clase cuya declaración está más adelante.
|
|
|