Métodos y variables estáticos
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 31 de 104.
Has aprendido que los miembros estáticos se comparten entre todos los objetos. Ahora exploremos cómo definir y utilizar correctamente métodos estáticos junto con variables estáticas.
Las variables estáticas declaradas dentro de una clase deben ser definidas fuera de la clase, normalmente en un archivo de código fuente. Esto se debe a que los miembros estáticos necesitan exactamente una ubicación de almacenamiento compartida por todos los objetos:
class Counter {
static int count; // Solo declaración
public:
Counter() { count++; }
~Counter() { count--; }
static int getCount() { // Método estático
return count;
}
};
// Definición - requerida fuera de la clase
int Counter::count = 0;Un método estático pertenece a la clase, no a ningún objeto. Esto tiene una consecuencia importante: los métodos estáticos no pueden acceder a los miembros de instancia ni usar el puntero this, porque no se llaman sobre ningún objeto específico.
class MathUtils {
static double pi;
public:
static double circleArea(double radius) {
return pi * radius * radius; // Puede acceder a miembros estáticos
}
};
double MathUtils::pi = 3.14159;
// Llamado sin un objeto
double area = MathUtils::circleArea(5.0);Los métodos estáticos son ideales para funciones de utilidad que no necesitan el estado del objeto, métodos de fábrica que crean objetos o para acceder a datos estáticos. Dado que no requieren una instancia de objeto, se llaman utilizando el nombre de la clase y el operador de resolución de ámbito: ClassName::methodName().
Desafío
FácilConstruyamos un sistema generador de IDs únicos que demuestre cómo los métodos y variables estáticos trabajan juntos para proporcionar funcionalidad a nivel de clase sin necesidad de instancias de objetos.
Crearás dos archivos para organizar tu código:
IDGenerator.h: Define una claseIDGeneratorque genere IDs únicos secuenciales. Tu clase debe tener:- Un miembro estático privado
nextIdque rastree el siguiente ID a asignar (comienza en1000) - Un miembro estático privado
prefix(string) que almacene un prefijo personalizable para todos los IDs - Un método estático
setPrefix(const std::string& p)que cambie el prefijo - Un método estático
getNextId()que devuelva un string combinando el prefijo y el número de ID actual, y luego incrementenextIdpara la siguiente llamada - Un método estático
peekNextId()que devuelva cuál sería el siguiente ID sin incrementar el contador - Un método estático
getGeneratedCount()que devuelva cuántos IDs se han generado (pista: calcúlalo a partir del valor inicial y elnextIdactual) - Un método estático
reset()que restablezcanextIda1000
Recuerda definir tus miembros estáticos fuera de la clase después de la definición de la clase. Inicializa
prefixa"ID-".- Un miembro estático privado
main.cpp: Demuestra la llamada a métodos estáticos utilizando el nombre de la clase sin crear ningún objeto. Lee un prefijo personalizado de la entrada, luego:- Imprime
"Peek: <peeked_id>"usandopeekNextId() - Genera tres IDs usando
getNextId()e imprime cada uno como"Generated: <id>" - Imprime
"Count: <count>"mostrando cuántos IDs se han generado - Cambia el prefijo usando el valor de entrada con
setPrefix() - Genera dos IDs más e imprime cada uno como
"Generated: <id>" - Imprime
"Count: <count>"de nuevo - Llama a
reset() - Imprime
"After reset - Peek: <peeked_id>" - Imprime
"After reset - Count: <count>"
- Imprime
Observa que nunca creas un objeto IDGenerator; todos los métodos se llaman directamente en la clase usando IDGenerator::methodName(). Esta es la característica clave de los métodos estáticos: pertenecen a la clase misma, no a ninguna instancia en particular.
Incluye <string> e <iostream> en tu archivo de cabecera, y no olvides las guardas de cabecera (header guards).
Hoja de referencia
Las variables estáticas declaradas dentro de una clase deben ser definidas fuera de la clase. Los miembros estáticos necesitan exactamente una ubicación de almacenamiento compartida por todos los objetos:
class Counter {
static int count; // Solo declaración
public:
Counter() { count++; }
static int getCount() {
return count;
}
};
// Definición - requerida fuera de la clase
int Counter::count = 0;Un método estático pertenece a la clase, no a ningún objeto. Los métodos estáticos no pueden acceder a miembros de instancia ni usar el puntero this porque no se llaman sobre ningún objeto específico:
class MathUtils {
static double pi;
public:
static double circleArea(double radius) {
return pi * radius * radius; // Puede acceder a miembros estáticos
}
};
double MathUtils::pi = 3.14159;
// Llamado sin un objeto usando el nombre de la clase
double area = MathUtils::circleArea(5.0);Los métodos estáticos se llaman utilizando el nombre de la clase y el operador de resolución de ámbito: ClassName::methodName(). Son ideales para funciones de utilidad que no necesitan el estado del objeto, métodos de fábrica o para acceder a datos estáticos.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "IDGenerator.h"
using namespace std;
int main() {
// Lee el prefijo personalizado de la entrada
string customPrefix;
cin >> customPrefix;
// TODO: Imprime "Peek: <peeked_id>" usando peekNextId()
// TODO: Genera tres IDs usando getNextId() e imprime cada uno como "Generated: <id>"
// TODO: Imprime "Count: <count>" mostrando cuántos IDs se han generado
// TODO: Cambia el prefijo usando el valor de entrada con setPrefix()
// TODO: Genera dos IDs más e imprime cada uno como "Generated: <id>"
// TODO: Imprime "Count: <count>" de nuevo
// TODO: Llama a reset()
// TODO: Imprime "After reset - Peek: <peeked_id>"
// TODO: Imprime "After reset - Count: <count>"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico