El puntero this
Para cada objeto declarado de una clase se mantiene una copia de sus datos, pero todos comparten la misma copia de las funciones de esa clase.
Esto ahorra memoria y hace que los programas ejecutables sean más compactos, pero plantea un problema.
Cada función de una clase puede hacer referencia a los datos de un objeto, modificarlos o leerlos, pero si sólo hay una copia de la función y varios objetos de esa clase, ¿cómo hace la función para referirse a un dato de un objeto en concreto?
La respuesta es: usando el puntero especial llamado this. Se trata de un puntero que tiene asociado cada objeto y que apunta a si mismo. Ese puntero se puede usar, y de hecho se usa, para acceder a sus .
Volvamos al ejemplo de la clase pareja:
#include <iostream>
using namespace std;
class pareja {
public:
// Constructor
pareja(int a2, int b2);
// Funciones miembro de la clase "pareja"
void Lee(int &a2, int &b2);
void Guarda(int a2, int b2);
private:
// Datos miembro de la clase "pareja"
int a, b;
}; |
Para cada dato podemos referirnos de dos modos distintos, lo veremos con la función Guarda. Esta es la implementación que usamos en el capítulo 29, que es como normalmente nos referiremos a los de las clases:
void pareja::Guarda(int a2, int b2) {
a = a2;
b = b2;
} |
Veamos ahora la manera equivalente usando el puntero this:
void pareja::Guarda(int a2, int b2) {
this->a = a2;
this->b = b2;
} |
Veamos otro ejemplo donde podemos aplicar el operador this. Se trata de la aplicación más frecuente, como veremos al implementar el constructor copia, o al sobrecargar ciertos operadores.
A veces necesitamos invocar a una función de una clase con una referencia a un objeto de la misma clase, pero las acciones a tomar serán diferentes dependiendo de si la referencia que pasamos se refiere al mismo objeto o a otro diferente, veamos cómo podemos usar el puntero this para determinar esto:
#include <iostream>
using namespace std;
class clase {
public:
clase() {}
void EresTu(clase& c) {
if(&c == this) cout << "Sí, soy yo." << endl;
else cout << "No, no soy yo." << endl;
}
};
int main() {
clase c1, c2;
c1.EresTu(c2);
c1.EresTu(c1);
return 0;
} |
La función "EresTu" recibe una referencia a un objeto de la clase "clase". Para saber si se trata del mismo objeto, comparamos la dirección del objeto recibido con el valor de this, si son la misma, es que se trata del mismo objeto.
No, no soy yo.
Sí, soy yo.
Este puntero nos resultará muy útil en próximos capítulos, en los que nos encontraremos situaciones en las que es imprescindible su uso.
Cómo funciona
Para intentar comprender algo mejor cómo funciona este puntero, veremos una forma de simularlo usando un ejemplo con estructuras.
Intentaremos crear una estructura sencilla que tenga un miembro con un puntero a un objeto de su mismo tipo. Además, haremos que ese puntero contenga la dirección del propio objeto. De modo que si creamos varios objetos de esa clase, cada uno de ellos apunte a si mismo.
Después de intentarlo un rato, comprobaremos que no es posible hacer esto dentro del constructor, ya que en ese momento no tenemos referencias al objeto. Tendremos que hacerlo, pues, añadiendo un parámetro más al constructor o después de crear el objeto, añadiendo una línea obj.esto = &obj;.
#include <iostream>
using namespace std;
struct ejemplo {
ejemplo(int v, ejemplo* e);
int valor;
ejemplo *esto;
};
ejemplo::ejemplo(int v, ejemplo *e) : valor(v), esto(e) {}
int main() {
ejemplo e1(19, &e1);
//e1.esto = &e1;
cout << &e1 << " " << (void*)e1.esto << endl;
return 0;
}
|
Ejecutar este código en codepad.
Bien, esto es lo que hace el compilador en nuestro lugar cuando crea el puntero this. Y lo pongo en cursiva porque en realidad ese puntero no se almacena junto con el objeto. No ocupa memoria, y por lo tanto no se tiene en cuenta al calcular el tamaño de la estructura con el operador sizeof, no se crea, ni se iniciliza. El compilador sabe durante la fase de compilación dónde almacena cada objeto, y por lo tanto, puede sustituir las referencias al puntero this por sus valores en cada caso.
El puntero this nos permite hacer cosas como obtener una referencia al propio objeto como valor de retorno en determinadas funciones u operadores. En ocasiones puede resolver ambigüedades o aclarar algún código.
Palabras reservadas usadas en este capítulo
this.
Comentarios