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 métodos estáticos junto con variables estáticas.
Las variables estáticas declaradas dentro de una clase deben definirse fuera de la clase, normalmente en un archivo 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 - necesaria 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 en ningún objeto específico.
class MathUtils {
static double pi;
public:
static double circleArea(double radius) {
return pi * radius * radius; // Puede acceder a los miembros estáticos
}
};
double MathUtils::pi = 3.14159;
// Se llama 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ácilVamos a crear 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 necesidad de 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á (empieza en1000) - Un miembro estático privado
prefix(string) 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 un string que combine el prefijo y el número de ID actual; 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 el valor actual denextId) - Un método estático
reset()que restablezcanextIda1000
Recuerda definir los 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 objetos. 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>"para mostrar 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>" - Vuelve a imprimir
"Count: <count>" - Llama a
reset() - Imprime
"After reset - Peek: <peeked_id>" - Imprime
"After reset - Count: <count>"
- Imprime
Fíjate en 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 propia clase, no a ninguna instancia en particular.
Incluye <string> y <iostream> en tu archivo de encabezado, y no olvides las guardas del encabezado.
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>" para mostrar 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 la POO
Archivos externosCompilación y construcción en C++Archivos de cabecera y archivos fuenteEspacios de nombres y ámbitoIntroducción a la POO en C++Clases y objetosEl puntero 'this'Métodos (funciones miembro)Atributos (miembros de datos)Fundamentos de constructores y destructoresRepaso: calculadora sencilla4Propiedades de clase
Miembros de instancia vs. estáticosGetters y settersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases friendRepaso: gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de constructores y destructoresSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualRepaso: jerarquía de empleados2Gestión de memoria
Memoria de pila vs. heapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Repaso: gestor de arreglos dinámicos5Encapsulación
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultación de informaciónStruct vs. classClases anidadas e internasRepaso: sistema de registros de estudiantes8Polimorfismo
Polimorfismo en compilación y en tiempo de ejecuciónSobrecarga de funcionesRepaso de las funciones virtualesFunciones virtuales purasClases abstractasDiseño de interfaces en C++Conversión dinámica y RTTIRepaso: calculadora de formas11Conceptos avanzados de POO
Composición frente a herenciaMixins mediante CRTPIdiom PimplBorrado de tiposClases enum 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 predeterminadoConstructor con parámetrosConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis detallado del destructorRegla de tres / cinco / ceroRepaso: clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujoSobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tipoRepaso: clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasFundamentos de SFINAE y rasgos de tiposRepaso: contenedor genéricoPractica por tu cuenta: Compilador de C++ online