Usa el puntero this en C++

Muhammad Husnain 12 octubre 2023
  1. Usa el puntero this en C++
  2. Diferencia entre this y *this en C++
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.

Nota
El puntero 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.

Muhammad Husnain avatar Muhammad Husnain avatar

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

Artículo relacionado - C++ Pointer