Aquí te cuento sobre las dos.
FUNCIONES AMIGAS
Una función amiga de una clase es una función cualquiera que puede acceder a los atributos y métodos privados de esa clase, como si fuese un método interno
de la clase. Veamos dos ejemplos que ilustren "la amistad":
---FUNCIÓN SIN AMISTAD
#include <iostream>
#include <string>
using namespace std;
class ID
{
string nombre;
int numero_telf;
public:
ID(string nom="", int nt=0) {nombre=nom, numero_telf=nt;}
string getNombre() { return nombre; }
void setNombre(string nom) { nombre = nom; }
int getNumeroTelf() { return numero_telf; }
void setNumeroTelf(int nt) { numero_telf = nt; }
};
void visualizar(ID objID);
int main()
{
ID a("Anita",23293);
visualizar(a);
return 0;
}
void visualizar(ID objID)
{
cout << "Nombre: " << objID.getNombre() << endl;
cout << "Numero Telefono: " << objID.getNumeroTelf() << endl;
}
Lo más importante es la función "visualizar".
Fíjate que utiliza los métodos de acceso "getNombre" y "getNumeroTelf" para obtener el valor de los atributos de la clase.
Veamos que ocurre si declaramos la función "visualizar" amiga de la clase "ID":
---FUNCIÓN AMIGA
#include <iostream>
#include <string>
using namespace std;
class ID
{
string nombre;
int numero_telf;
public:
ID(string nom="", int nt=0) {nombre=nom, numero_telf=nt;}
string getNombre() { return nombre; }
void setNombre(string nom) { nombre = nom; }
int getNumeroTelf() { return numero_telf; }
void setNumeroTelf(int nt) { numero_telf = nt; }
friend void visualizar(ID objID); // IMPORTANTE: ¡se declara dentro de la clase de forma pública!
};
int main()
{
ID a("Anita",23293);
visualizar(a);
return 0;
}
void visualizar(ID objID)
{
cout << "Nombre: " << objID.nombre<< endl;
cout << "Numero Telefono: " << objID.numero_telf<< endl;
}
Observa que ahora la función "visualizar" accede directamente a los atributos, sin necesidad de métodos de acceso. Es decir, cuando una función se declara
amiga de una clase, puede acceder a todos los atributos y métodos privados de ésta, como si de un método interno de la clase se tratara.
Ambos programas tienen el mismo efecto.
Pero en el segundo, se elimina la encapsulación que proporcionan los controles de acceso mediante la amistad.
Debe aclararse que la amistad no se hereda. Es decir, que una función amiga de una clase base no podrá acceder a los miembros privados (ni tampoco
protegidos, puesto que no se hereda la amistad) de una clase derivada.
Debe aclararse también que una función amiga de la clase derivada no podrá acceder a los métodos privados de la clase base, pero sí a los protegidos y
públicos, igual que cualquier método de la clase derivada.
FUNCIONES VIRTUALES
Una función virtual es un método de una clase base que puede ser redefinido para una clase derivada y llamado mediante un puntero o referencia a la clase
base, resolviéndose dicha llamada en función del tipo de objeto referenciado.
Veamos dos ejemplos.
---FUNCION NO VIRTUAL
#include <iostream>
#include <string>
using namespace std;
class Felino
{
protected:
string nombre;
int edad, peso;
public:
Felino(string nom = "", int ed = 0, int pe = 0) { nombre = nom, edad = ed, peso = pe; }
void Hablar() { cout << "Soy un animal cualquiera.\n"; }
};
class Gato : public Felino
{
public:
Gato(string nom = "", int ed = 0, int pe = 0) { nombre = nom, edad = ed, peso = pe;}
void Hablar() { cout << "Soy un gato\n"; }
};
class Tigre : public Felino
{
public:
Tigre(string nom = "", int ed = 0, int pe = 0) {nombre = nom, edad = ed, peso = pe;}
void Hablar() { cout << "Soy un tigre\n"; }
};
int main()
{
Felino *pF;
Gato g("Gatito",1,5);
Tigre t("TigreRabioso",7,120);
pF = &t;
pF->Hablar();
pF = &g;
pF->Hablar();
//Pausa...
char a;
cin >> a;
return 0;
}
Veamos que al ejecutar el programa, ambas llamadas al método hablar de los tipos Gato y Tigre
referenciados desde Felino se resuelven como llamadas a la clase del tipo del puntero (Felino).
Vamos a ver que ocurre si declaramos, en la clase base (Felino) el método Hablar como virtual.
---FUNCION VIRTUAL
#include <iostream>
#include <string>
using namespace std;
class Felino
{
protected:
string nombre;
int edad, peso;
public:
Felino(string nom = "", int ed = 0, int pe = 0) { nombre = nom, edad = ed, peso = pe; }
virtual void Hablar() { cout << "Soy un animal cualquiera.\n"; } // IMPORTANTE: ¡se añade "virtual" al principio de la declaración!
};
class Gato : public Felino
{
public:
Gato(string nom = "", int ed = 0, int pe = 0) { nombre = nom, edad = ed, peso = pe;}
void Hablar() { cout << "Soy un gato\n"; }
};
class Tigre : public Felino
{
public:
Tigre(string nom = "", int ed = 0, int pe = 0) {nombre = nom, edad = ed, peso = pe;}
void Hablar() { cout << "Soy un tigre\n"; }
};
int main()
{
Felino *pF;
Gato g("Gatito",1,5);
Tigre t("TigreRabioso",7,120);
pF = &t;
pF->Hablar();
pF = &g;
pF->Hablar();
//Pausa...
char a;
cin >> a;
return 0;
}
Aquí las cosas cambian. Ahora la salida se corresponde con cada uno de los tipos de los objetos referenciados, devolviendo
dos salidas distintas por cada llamada.
Una clase con métodos virtuales se llama "tipo polimórfico".
Esta es la esencia del polimorfismo en la programación orientada a objetos.
Espero que estos ejemplos te ayuden.
Zoto