Acerca del puntero implícito this

Puntero this


Última actualización del post: Diciembre de 2019

En C++, todo proceso llevado a cabo por una función miembro no-estática de una clase se realiza implícitamente sobre el objeto *this, siendo this el nombre del puntero que almacena la dirección en memoria del objeto que invocó a dicha función.

El tipo del puntero this será cv X*, siendo X el nombre de la clase a la que pertenezca la función miembro y cv los calificadores const-volatile que dicha función pudiese tener. Observemos, a este respecto, que los constructores y destructores no pueden acompañarse de dichos calificadores, por lo que el puntero this siempre será de tipo X* en su interior.

El puntero this puede ser empleado en el cuerpo de cualquier función miembro no-estática, en la lista de inicialización de un constructor y en los inicializadores por defecto de datos miembro. La indirección this-> es añadida implícitamente al comienzo del nombre de cualquier miembro no estático de la clase. Consideremos, por ejemplo, la siguiente función miembro de nombre reset_id() perteneciente a la interfaz pública de la clase Student:

   class Student {       int id_;       std::string name_;    public:       // constructores y resto de interfaz pública...       void reset_id(int id) { id_ = id; }    };

Podríamos referirnos explícitamente al objeto que invoque dicha función, si así lo deseáramos, a través del puntero this que lo referencia:

   void Student::reset_id(int id) { this->id_ = id; }

Por regla general, sin embargo, el uso explícito de tales referencias en situaciones como la indicada es redundante e innecesario, como resulta evidente tras una rápida comparación de ambas versiones del código.

Puede también emplearse el puntero this con el fin de evitar la colisión de nombres entre atributos y argumentos de función:

   class Student {       int id;       std::string name;    public:       // ...       void reset_id(int id) { this->id = id; }    };

Ejemplos como el anterior resultan, sin embargo, bastante artificiosos, pues el uso explícito del puntero this puede evitarse fácilmente a poco que empleemos un convenio de nombres apropiado para nuestros datos, como el utilizado en nuestra primera implementación de la clase Student.

Algunos casos de uso


Debemos resaltar la absoluta necesidad de recurrir al puntero this en otras circunstancias bien conocidas, como por ejemplo al devolver una referencia al objeto modificado en los operadores de asignación copia con tal de permitir la concatenación de asignaciones:

   Student& Student::operator=(Student const& other)    {       id_ = other.id_;       name_ = other.name_;       return *this;    }

Pueden encontrarse usos similares a éste en la definición de otros operadores como los de comparación.

Recordemos, asimismo, que un compilador 100% conforme con el estándar ISO del lenguaje no realizará una búsqueda de nombres en plantillas de clase base cuando intente localizar la procedencia de nombres no-dependientes en clases derivadas:

   template<typename T>    struct B {       T i{};    };    template<typename T>    struct D : B<T> {       void f() {          std::cout << i; // error de compilación: 'i' no fue declarado en este ámbito       }    };

Con el fin de evitar errores de compilación como el anterior, podemos emplear el puntero this:

   template<typename T>    struct B {       T i{};    };    template<typename T>    struct D : B<T> {       void f() {          std::cout << this->i; // ok       }    };

De forma alternativa, el uso del puntero this podría evitarse también en este caso a través de una cláusula using:

   template<typename T>    struct B {       T i{};    };    template<typename T>    struct D : B<T> {       // introducimos B<T>::i en la definición de D:       using B<T>::i;       void f() { std::cout << i; }    };

No hay comentarios:

Publicar un comentario