Usa el puntero this en C++
En este tutorial, primero entenderemos el concepto del puntero this
.
Luego, demostraremos sus usos con la ayuda de ejemplos. Por último, veremos en qué se diferencia this
de *this
.
Usa el puntero this
en C++
El puntero this
es simplemente un puntero implícitamente disponible que, en un ámbito de función de miembro de clase no estática, se refiere o apunta al objeto que llama. Si no entendiste por definición, veamos algunos fundamentos para entender el concepto.
Sabemos que todos los miembros de datos no estáticos tienen una copia separada para cada objeto. Sin embargo, el código de las funciones miembro se comparte entre todos los objetos.
Todos los objetos de la misma clase acceden a la misma definición de función desde el segmento de código cuando necesitan invocarlo. El compilador necesita conocer la información del objeto que llama (quién está llamando actualmente a una función miembro) para acceder o actualizar el miembro de datos correcto específico para este objeto de llamada en particular.
Entonces, dado que las definiciones de función son las mismas, ¿cómo obtiene el compilador la información del objeto que llama?
La respuesta simple es que durante la compilación del programa, el compilador agrega automáticamente un parámetro de puntero implícito oculto en las funciones miembro. Este puntero implícito se conoce como puntero this
.
Cada vez que se invoca una función miembro a través de un objeto de clase en particular, el objeto que llama se proporciona automáticamente como un argumento oculto para el puntero this
. Ahora, el compilador puede usar este puntero para acceder o modificar el miembro de datos correcto específico del objeto que llama.
this
no es necesario con los miembros estáticos de la clase, ya que estos miembros (funciones y miembros de datos) se comparten entre todos los objetos.La actualización o el acceso a los miembros estáticos no es específico de ningún objeto. Incluso podemos acceder a estos miembros sin crear un objeto de la clase.
Por lo tanto, el compilador no usa el operador this
con estos miembros.
Veamos el código de ejemplo para comprender por qué decimos que el compilador usa implícitamente el puntero this
y cuándo es útil.
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string Name;
public:
Person(string Name) {
this->Name = Name; // this->Name is the private member for this object
}
void PrintName() { cout << this->Name << endl; }
void PrintName1() { cout << Name << endl; }
};
int main() {
Person P("Alexa");
P.PrintName();
P.PrintName1();
return 0;
}
El ejemplo de código anterior define una clase Person
con dos funciones miembro diferentes para imprimir el miembro de datos privados Name
. La primera instrucción en la función main()
genera un objeto Person
y pasa Alexa
como argumento al constructor.
Ahora, this->Name
en el constructor ayuda al compilador a distinguir entre el parámetro local Name
y el miembro de datos privados Name
.
El código posterior de main()
llama a printName()
y printName1()
a través de P (es decir, P se convierte en el objeto que llama).
Producción :
Alexa
Alexa
La salida de ambas funciones es la misma. Esto se debe a que el compilador precede implícitamente a this->
con Name
en PrintName1()
.
Diferencia entre this
y *this
en C++
Hasta ahora, tenemos claro el concepto de que this
es un puntero a un objeto. Para un obj
de tipo Person
, this
es de tipo Persona*
.
Otra cosa a recordar es que el puntero this
siempre es un rvalue
, que no se puede modificar. Sin embargo, *this
elimina la referencia del puntero this
.
Después de pasar por suficiente experiencia, veamos un código de ejemplo para entender la diferencia entre this
y *this
.
#include <iostream>
using namespace std;
class Counter {
private:
int Count;
public:
Counter() { this->Count = 0; }
void IncreaseCount() { Count++; }
void PrintCount() { cout << this->Count << endl; }
Counter* GetCount_Pointer() { return this; }
Counter GetCount_Copy() { return *this; }
Counter& GetCount_Reference() { return *this; }
};
int main() {
// Section-A
cout << "Sectio-A" << endl;
Counter C1;
C1.IncreaseCount();
Counter* CounterPtr =
C1.GetCount_Pointer(); // CounterObj will be pointing to C1
CounterPtr->IncreaseCount();
C1.PrintCount();
// Section-B
cout << "Section-B" << endl;
Counter C2;
C2 = C1.GetCount_Copy();
C1.IncreaseCount();
C1.PrintCount();
C2.PrintCount();
// Section-C
cout << "Section-B" << endl;
Counter& CounterRef = C1.GetCount_Reference();
CounterRef.PrintCount();
return 0;
}
Este fragmento de código crea una clase Contador
con múltiples métodos para los métodos GetCount
. El primer método (es decir, GetCount_Pointer
) devuelve el valor de this
puntero que es simplemente una dirección del objeto que llama.
El método GetCount_Copy
devuelve *this
mientras que el tipo de retorno es un objeto de tipo Counter
. Por lo tanto, esta función devolverá una copia profunda del objeto que llama, lo que significa que la modificación de la copia devuelta no afectará a la copia original.
El último método GetCount_Reference
también devuelve *this
, pero el tipo de retorno de este método es un objeto de referencia (es decir, Counter&
). En lugar de crear una nueva copia en profundidad, el compilador devolverá un alias o una referencia al objeto original.
Cualquier cambio realizado a través del alias también se reflejará en el objeto original.
Antes de discutir el código del controlador principal, veamos la salida del programa.
Producción :
Sectio-A
2
Section-B
3
2
Section-B
3
La sección-A del método main
declara primero el objeto C1
de tipo Contador
e incrementa su Count
mediante IncreaseCount()
. Después, inicializa un puntero CounterPtr
con la dirección devuelta por C1.GetCount_Pointer()
.
Ahora, el CounterPtr
estará apuntando a C1
. Por lo tanto, llamar a la función de incremento a través de CounterPtr
también modifica el C1
.
C2 = C1.GetCount_Copy()
en la sección B copia profundamente todo el contenido actual de C1
a C2
ya que C1.GetCount_Copy()
se reemplaza con una copia de C1
. Por lo tanto, el aumento de la cuenta de C1
no afecta a C2
.
La sección-C declara una variable de referencia de tipo Contador
y la inicializa con lo que devuelva C1.GetCount_Reference()
. Como C1.GetCount_Reference()
devuelve un alias de C1
, por lo tanto, CounterRef
se convierte en otro nombre para C1
.
La sentencia Counter& CounterRef = C1.GetCount_Reference();
se vuelve lógicamente equivalente a Counter& CounterRef = C1
.
Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.
LinkedIn