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 usar correctamente los métodos estáticos junto con las variables estáticas.
Las variables estáticas declaradas dentro de una clase deben definirse 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 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 de un objeto, métodos de fábrica que crean objetos o para acceder a datos estáticos. Como no requieren una instancia de objeto, los llamas usando el nombre de la clase y el operador de resolución de ámbito: ClassName::methodName().
Desafío
FácilConstruyamos un sistema generador de ID únicos que demuestre cómo los métodos y las variables estáticos trabajan juntos para proporcionar funcionalidad a nivel de clase sin necesitar instancias de objetos.
Crearás dos archivos para organizar tu código:
IDGenerator.h: Define una claseIDGeneratorque genere ID únicos secuenciales. Tu clase debe tener:- Un miembro estático privado
nextIdque registre el siguiente ID que se asignará (comienza en1000) - Un miembro estático privado
prefix(cadena) que almacene un prefijo personalizable para todos los ID - Un método estático
setPrefix(const std::string& p)que cambie el prefijo - Un método estático
getNextId()que devuelva una cadena que combine el prefijo y el número de ID actual, y después 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 ID se han generado (pista: calcúlalo a partir del valor inicial y delnextIdactual) - 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
prefixcon"ID-".- Un miembro estático privado
main.cpp: Demuestra cómo llamar a métodos estáticos usando el nombre de la clase sin crear ningún objeto. Lee un prefijo personalizado de la entrada y después:- Imprime
"Peek: <peeked_id>"usandopeekNextId() - Genera tres ID usando
getNextId()e imprime cada uno como"Generated: <id>" - Imprime
"Count: <count>"mostrando cuántos ID se han generado - Cambia el prefijo usando el valor de entrada con
setPrefix() - Genera dos ID más e imprime cada uno como
"Generated: <id>" - Imprime
"Count: <count>"nuevamente - 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 en sí, no a ninguna instancia concreta.
Incluye <string> y <iostream> en tu archivo de cabecera y no olvides las protecciones de inclusión.
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: Print "After reset - Peek: <peeked_id>"
// TODO: Print "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éricoPractica por tu cuenta: Compilador de C++ online