Referencias

Las referencias Una referencia es otra forma de acceder a un dato, una especie de alias. Cualquier operacion sobre una referencia afectara a ese dato al que hace referencia.

Figura: sin duda los punteros y las referencias fueron obra de los sarracenos.

Veamos un ejemplo simple:

/**
* Referencias.cpp
* Programa que muestra el uso de referencias
* 
* Pello Xabier Altadill Izura 
*
* Compilado: g++ Referencias.cpp -o Referencias
*/

using namespace std;
#include <iostream>

int main() {

	// Definimos un dato y su referencia
	int numero;
	int &referenciaNumero = numero; // Ahi se crea la referencia
	
	cout << "Vamos a ver que pasa si le asignamos un dato: " << endl;

	numero = 31337;
	
	// Los dos mostraran el mismo valor
	cout << "Valor de numero: " << numero << endl;
	cout << "Valor de referenciaNumero: " << referenciaNumero << endl;
	
	// y a donde apuntan? AL MISMO SITIO
	cout << "Posicion de numero: " << &numero << endl;
	cout << "Posicion de referenciaNumero: " << &referenciaNumero << endl;
	cout << "Programa terminado \n" << endl;
	
	return 0;

}

Con los objetos se pueden hacer referencias igualmente:

Objeto miObjeto;
Objeto &refObjeto = miObjeto;

Referencias y funciones Vamos a ver distintas formas de pasar referencias a una funcion. Como en c, podemos pasar parametros por referencia y hacer que esos parametros contengan resultados de una funcion.

/**
* ReferenciaFunciones.cpp
* Programa que muestra el uso de referencias en las funciones
* 
* Pello Xabier Altadill Izura 
*
* Compilado: g++ ReferenciaFunciones.cpp -o ReferenciaFunciones
*/

using namespace std;
#include <iostream>

// 1º funcion que intercambia dos valores
void exchange (int *refa, int *refb);

// 2º funcion -sobrecargada- que intercambia dos valores
void exchange (int &refa, int &refb);


int main() {
	
	// Definimos un dato y su referencia
	int a, b;
	
	cout << "Asignamos valores: " << endl;
	a = 45;
	b = 21;
	
	cout << "Valores: a=" << a << " b=" << b << endl;
	cout << "Hacemos intercambio con exchange(int *refa, int *refb): " << endl;

	exchange(&a, &b); // Con esta llamada invocamos la primera funcion!!
	
	cout << "Valores: a=" << a << " b=" << b << endl;
	cout << "Hacemos intercambio con exchange(int &refa, int &refb): " << endl;

	xchange(a, b); // Con esta llamada invocamos la segunda funcion!!
	
	out << "Valores: a=" << a << " b=" << b << endl;
	out << "Programa terminado \n" << endl;

	return 0;

}

// 1º funcion que intercambia dos valores
void exchange (int *refa, int *refb) {

	int tmp;

	tmp = *refa;
	*refa = *refb;
	*refa = tmp; 

}


// 2º funcion -sobrecargada- que intercambia dos valores
void exchange (int &refa, int &refb) {
	
	int tmp;
	
	tmp = refa;
	refa = refb;
	refa = tmp;

}

Pasando clases por referencia

/**
* Gremlin.hpp
*
* Clase que representa el objeto Gremlin.
* Observese el 3º metodo constructor
* Pello Xabier Altadill Izura 
*
*/

using namespace std;
#include <iostream>

class Gremlin {

public:

	Gremlin();

	Gremlin(char *nmb,int ed, int p);
	
	Gremlin(Gremlin&); // atencion a este constructor
	
	~Gremlin();

	void correr();

	void dormir();

	void morder();

	int peso;

private:
	
	char *nombre;
	
	int edad;

};

Y su implementacion:

/**
* Gremlin.cpp
*
* Clase que implementa el objeto Gremlin.
* Pello Xabier Altadill Izura 
*
*/

#include "Gremlin.hpp"

Gremlin::Gremlin() {

	peso = 1;

	cout << "Gremlin creado." << endl;

}


Gremlin::Gremlin (char *nmb,int ed, int p) {
	
	nombre = nmb;
	edad = ed;
	peso = p;
}


Gremlin::~Gremlin() {
	
	cout << "Aaaargh!\nGremlin destruido." << endl;

}



// El gremlin corre
void correr() {
	
	cout << "Jaja grrrr!! jajaja!" << endl;

}


// El gremlin duerme
void dormir() {

	cout << "zzzZZZZzzzzz" << endl;

}


// El gremlin muerde
void morder() {

	cout << "roaar &ntilde;am &ntilde;am" << endl; 

}


// Definimos esta funcion aparte de la clase
// Con ella el gremlin come y aumenta su atributo peso.
void comer (Gremlin *g) {

	// Invocamos la mordedura para que coma
	g->morder();

	// Le aumentamos 3 unidades por comer
	g->peso += 3;

}


// Funcion main
int main () {

	cout << "Iniciando programa. " << endl;
	
	// Definimos un gremlin
	Gremlin tbautista;

	// y lo movemos por la ciudad
	tbautista.correr();
	tbautista.morder();

	// Mostramos su peso
	cout << "El gremlin pesa: " << tbautista.peso << endl;

	// Le hacemos comer:
	comer(&tbautista);

	// Mostramos su peso otra vez
	cout << "El gremlin pesa ahora: " << tbautista.peso << endl;
	cout << "Finalizando programa\n " << endl;

	return 0;

}

La ventaja que logramos al pasar parametros por referencia es que ahorramos espacio en memoria ya que sino en cada llamada a una funcion se hacen copias de los parametros. Esto tambien tiene una desventaja: si le pasamos a una funcion el ORIGINAL de un objeto (con una referencia) en lugar de una copia corremos el riesgo de que la funciona haga trizas nuestro objeto y perder el "original" (supongamos que la funcion esta hecha por terceros y no sabemos lo que hace). Que se puede hacer para salvaguardar nuestros objetos? Punteros constantes Esta es la solucion: pasar punteros constantes. Eso hara que la funcion solo tenga permiso para invocar los metodos constantes de la clase. SE cambia un poco la clase gremlin para mostrar esto.

/**
* Gremlin2.hpp
*
* Clase que representa el objeto Gremlin.
* Con un metodo definido como const!!
* Pello Xabier Altadill Izura 
*
*/

using namespace std;
#include <iostream>

class Gremlin {

public:

	Gremlin();

	Gremlin(char *nmb,int ed, int p);

	Gremlin(Gremlin&); // atencion a este constructor

	~Gremlin();

	void correr();

	void dormir();

	void morder();

	// Definimos una funcion constante
	char * getNombre() const;

	int peso;

private:
	
	char *nombre;
	
	int edad;

};

Y vemos la implementacion en la que simplemente se puede observar como se protege el objeto en la funcion comer() gracias al uso de punteros constantes.

/**
* Gremlin2.cpp
*
* Clase que implementa el objeto Gremlin.
* Pello Xabier Altadill Izura 
*
*/

#include "Gremlin2.hpp"

Gremlin::Gremlin() {
	
	peso = 1;
	
	cout << "Gremlin creado." << endl;

}


Gremlin::Gremlin (char *nmb,int ed, int p) {
	
		nombre = nmb;
		edad = ed;
		peso = p;

}


Gremlin::~Gremlin() {
	
	cout << "Aaaargh!\nGremlin destruido." << endl;

}


// El gremlin corre
void correr() {
	
	cout << "Jaja grrrr!! jajaja!" << endl;

}


// El gremlin duerme
void dormir() {
	
	cout << "zzzZZZZzzzzz" << endl;

}


// El gremlin muerde
void morder() {
	
		cout << "roaar &ntilde;am &ntilde;am" << endl; 

}


// FUNCION CONST!!!
// Devuelve el nombre del gremlin
char * getNombre() const {

		return nombre;

}


// Definimos esta funcion aparte de la clase
// Con ella el gremlin come y aumenta su atributo peso.
void comer (const Gremlin const *g) {

	// Invocamos la mordedura para que coma??
	// g->morder(); ERROR no podemos invocar una funcion NO CONSTANTE!!!
	// en cambio si podemos invocar getNombre
	cout << "Nombre" << g->getNombre() << endl; 

}


// Funcion main
int main () {

	cout << "Iniciando programa. " << endl;

	// Definimos un gremlin
	Gremlin tbautista;
	
	// y lo movemos por la ciudad
	tbautista.correr();
	tbautista.morder();
	
	// Mostramos su peso
	cout << "El gremlin pesa: " << tbautista.peso << endl;

	// Le hacemos comer:
	comer(&tbautista);
	
	// Mostramos su peso otra vez
	cout << "El gremlin pesa ahora: " << tbautista.peso << endl;
	cout << "Finalizando programa\n " << endl;

	return 0;

}