- Fundamentos de la programación orientada a objetos: Abstracción
- Fundamentos de la programación orientada a objetos: Encapsulación
- Fundamentos de la programación orientada a objetos: Herencia
- Fundamentos de la programación orientada a objetos: Polimorfismo
- Fundamentos de la programación orientada a objetos: Composición (diseño)
- Definición de clases
- Derechos de acceso
- Constructores: por defecto, paramétricos y de copia
- Destructores
- Autorreferencia: esto
- Herencia
- Creación dinámica de objetos: nuevo y suprimir
- Punteros
- Métodos virtuales (virtual y override)
- Miembros estáticos
- Tipos anidados, espacios de nombres y operador de contexto '::'
- Dividir definición y declaración de clase
- Clases abstractas e interfaces
- Sobrecarga de operadores
- Conversión de tipos de objeto: dynamic_cast y puntero void *
- Punteros, referencias y const
- Gestión de la herencia: final y delete
Autorreferencia: esto
En el contexto de cada clase, en su código de métodos, hay una referencia especial al objeto actual: this.. Básicamente, se trata de una variable definida implícitamente y se le pueden aplicar todos los métodos de trabajo con variables de objeto. En concreto, puede desreferenciarse para que se refiera a un campo de objeto o para llamar a un método. Por ejemplo, las siguientes sentencias en un método de la clase Shape son idénticas (utilizamos el método draw sólo a efectos de demostración):
class Shape
|
Podría ser necesario utilizar la forma larga si hay otras variables o parámetros con el mismo nombre en el mismo contexto. Esta práctica no suele ser bienvenida, pero si es necesario, la palabra clave this permite hacer referencia a los miembros anulados de un objeto.
El compilador emite un aviso si el nombre de cualquier variable local o parámetro de método se solapa con el nombre de una variable de miembro de clase.
En el siguiente ejemplo hipotético hemos implementado el método draw, que toma un parámetro de cadena opcional backgroundColor con el nombre del color. Como el nombre del parámetro es el mismo que el del miembro de la clase Shape, el compilador emite el primer aviso «la definición de 'backgroundColor' oculta el campo».
La consecuencia del solapamiento es que la posterior asignación errónea del valor clrBlue funciona en el parámetro y no en el miembro de la clase, y como los tipos del valor y del parámetro no coinciden, el compilador emitirá un segundo aviso: «conversión implícita de número a cadena» (el número aquí es una constante clrBlue). Pero la línea this.backgroundColor = clrBlue escribe el valor en el campo del objeto.
void draw(string backgroundColor = NULL) //warning 1:
|
La definición posterior de la variable booleana local backgroundColor (en el bloque anidado de llaves) anula de nuevo las definiciones anteriores de ese nombre (por eso recibimos el tercer aviso). Sin embargo, al anular la referencia a this, la sentencia this.backgroundColor = clrRed también hace referencia a un campo de objeto.
Sin this especificado, el compilador siempre elige la definición de nombre más cercana (por contexto).
También se necesita otro tipo de this: para pasar el objeto actual como parámetro a otra función. En concreto, se adopta un enfoque en el que objetos de la misma clase son responsables de crear o eliminar objetos de otra clase, y el objeto subordinado debe conocer a su «jefe». Seguidamente se crean los objetos dependientes en la clase «jefe» utilizando el constructor, y se le pasa this del objeto «jefe». Esta técnica suele utilizar la asignación dinámica de objetos y punteros, y debido a ello se mostrará un ejemplo relevante en la sección punteros.
Otro uso común de this es devolver un puntero al objeto actual desde una función miembro, lo que le permite organizar las llamadas a funciones miembro en una cadena. Como todavía tenemos que estudiar los punteros en detalle, bastará con saber que un puntero a un objeto de alguna clase se describe añadiendo el carácter '*' al nombre de la clase, y que se puede trabajar con un objeto a través de un puntero de la misma forma que se haría directamente.
Por ejemplo, podemos proporcionar al usuario varios métodos para establecer las propiedades de una forma individualmente: cambiar color, mover horizontal o mover verticalmente. Cada uno de ellos devolverá un puntero al objeto actual.
Shape *setColor(const color c)
|
A continuación es posible organizar convenientemente las llamadas a estos métodos en una cadena.
Shape s;
|
Cuando hay muchas propiedades en una clase, este enfoque le permite configurar un objeto de forma compacta y selectiva.
En la sección Definición de clases intentamos registrar una variable de objeto, pero descubrimos que podíamos utilizar su nombre con sólo un ampersand (en una llamada a Print) para obtener un puntero o, de hecho, un número único (manejador o handle). En el contexto de un objeto, el mismo manejador está disponible a través de &this.
A efectos de depuración, puede identificar los objetos por su descriptor. Vamos a explorar la herencia de clases, y cuando hay más de una, la identificación nos resultará muy útil. Por ello, en todos los constructores y destructores, añadimos (y añadiremos en el futuro en las clases derivadas) la siguiente llamada a Print:
~Shape()
|
Ahora todos los pasos de creación y eliminación se marcarán en el registro con el nombre de la clase y el número de objeto.
Implementamos constructores y destructores similares en la estructura Pair; sin embargo, en las estructuras, por desgracia, no se admiten los punteros, es decir, escribir &this es imposible. Por tanto, sólo podemos identificarlos por su contenido (en este caso, por sus coordenadas):
struct Pair
|