El puntero 'Self'
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 7 de 61.
En lenguajes orientados a objetos como Python o Java, cuando llamas a object.method(), el lenguaje pasa automáticamente el objeto mismo al método. En C, debemos hacer esto manualmente pasando un puntero a la estructura como primer argumento.
Este puntero se denomina convencionalmente self (tomado de Python) o this (de C++). Le da a la función acceso a los datos de la estructura para que pueda leerlos o modificarlos:
typedef struct {
int health;
} Player;
void player_take_damage(Player *self, int amount) {
self->health -= amount;
}
El puntero self es lo que hace que la función actúe como un método; sabe cuál jugador modificar. Sin él, la función no tendría forma de acceder a la instancia específica del struct. Al llamar a esta función, pasas la dirección de tu struct:
Player hero = {100};
player_take_damage(&hero, 25);
// hero.health ahora es 75
Usar un puntero (en lugar de pasar el struct directamente) es esencial para la modificación. Si pasaras el struct por valor, la función recibiría una copia y cualquier cambio se perdería cuando la función retorne. El puntero asegura que los cambios persistan en el struct original.
Desafío
FácilVamos a construir un módulo Timer que rastrea los segundos transcurridos. Practicarás el uso del patrón de puntero "self" para modificar el estado interno de un struct a través de funciones que actúan como métodos.
Crearás tres archivos:
timer.h: Declara un structTimercon un único miembroint seconds. También declara dos funciones:timer_tick(toma un puntero a Timer y añade un número especificado de segundos) ytimer_reset(toma un puntero a Timer y vuelve a poner los segundos a cero). Usa protectores de inclusión (include guards) con el símboloTIMER_H.timer.c: Implementa ambas funciones. La funcióntimer_tickdebe aceptar unTimer *selfy unint amount, luego añadir la cantidad a los segundos del temporizador. La funcióntimer_resetdebe aceptar unTimer *selfy establecer sus segundos a cero.main.c: Crea un Timer, realiza operaciones sobre él y muestra los resultados.
Recibirás dos entradas enteras: los segundos iniciales para tu temporizador y la cantidad a añadir mediante el tick.
En tu archivo principal:
- Inicializa un
Timercon el valor de segundos iniciales - Llama a
timer_tickpara añadir la cantidad especificada - Imprime los segundos actuales
- Llama a
timer_resetpara reiniciar el temporizador - Imprime los segundos de nuevo (debería ser 0)
Imprime los resultados en este formato:
After tick: {seconds}
After reset: {seconds}Por ejemplo, con un valor inicial de 30 y añadiendo 15 segundos, la salida sería:
After tick: 45
After reset: 0Hoja de referencia
En C, las funciones que operan sobre structs necesitan acceso explícito a la instancia del struct a través de un parámetro de puntero, convencionalmente llamado self o this:
typedef struct {
int health;
} Player;
void player_take_damage(Player *self, int amount) {
self->health -= amount;
}
El puntero self permite que la función acceda y modifique los datos del struct. Al llamar a tales funciones, pase la dirección de su struct:
Player hero = {100};
player_take_damage(&hero, 25);
// hero.health ahora es 75
El uso de un puntero es esencial para la modificación; pasar por valor crearía una copia y los cambios se perderían cuando la función retorne. El puntero asegura que los cambios persistan en el struct original.
Pruébalo tú mismo
#include <stdio.h>
#include "timer.h"
int main() {
int initial_seconds, amount_to_add;
scanf("%d", &initial_seconds);
scanf("%d", &amount_to_add);
// TODO: Crear un Timer e inicializarlo con initial_seconds
// TODO: Llamar a timer_tick para añadir amount_to_add segundos
// TODO: Imprimir los segundos actuales en formato: "After tick: {seconds}"
// TODO: Llamar a timer_reset para reiniciar el temporizador
// TODO: Imprimir los segundos de nuevo en formato: "After reset: {seconds}"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo