Miembros de instancia vs. estáticos
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 27 de 104.
Hasta ahora, cada variable miembro que has creado pertenece a un objeto específico. Cuando creas dos objetos Player, cada uno tiene su propio name y health. Estos se llaman miembros de instancia: existen por separado para cada instancia de la clase.
Pero ¿y si necesitas datos compartidos entre todos los objetos? Por ejemplo, llevar un registro de cuántos jugadores hay en total.
Aquí es donde entran en juego los miembros estáticos. Un miembro estático pertenece a la propia clase, no a ningún objeto en particular.
class Player {
std::string name; // Miembro de instancia: cada jugador tiene el suyo
int health; // Miembro de instancia
static int playerCount; // Miembro estático: compartido por TODOS los jugadores
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Incrementar cuando se crea cualquier jugador
}
~Player() {
playerCount--; // Decrementar cuando se destruye cualquier jugador
}
};La diferencia clave: se accede a los miembros de instancia a través de un objeto, mientras que se puede acceder a los miembros estáticos mediante el nombre de la clase. Piensa en los miembros de instancia como «uno por objeto» y en los miembros estáticos como «uno por clase».
| Miembros de instancia | Miembros estáticos |
|---|---|
| Únicos para cada objeto | Compartidos entre todos los objetos |
| Se crean cuando se crea el objeto | Existen antes de que se cree cualquier objeto |
Se accede a ellos mediante el objeto: player.health | Se accede a ellos mediante la clase: Player::playerCount |
Los miembros estáticos son útiles para contadores, valores de configuración o cualquier dato que pertenezca lógicamente a la clase en lugar de a los objetos individuales.
Desafío
FácilVamos a crear un sistema de inventario de productos que haga un seguimiento tanto de los detalles de cada producto como de las estadísticas generales del inventario mediante miembros de instancia y estáticos.
Crearás dos archivos para organizar tu código:
Product.h: Define una claseProductque gestione productos individuales y también haga un seguimiento de los datos de todo el inventario. Tu clase debe tener:- Miembros de instancia:
name(string) yprice(double): únicos para cada producto - Un miembro estático
totalProductsque cuente cuántos productos existen actualmente - Un miembro estático
totalValueque haga un seguimiento del precio combinado de todos los productos - Un constructor que reciba un nombre y un precio, inicialice los miembros de instancia y actualice ambos contadores estáticos (incrementar el recuento, añadir el precio al valor total)
- Un destructor que actualice los contadores estáticos cuando se elimine un producto (decrementar el recuento, restar el precio del valor total)
- Un método
getInfo()que devuelva una cadena con el formato:"<name>: $<price>" - Un método estático
getInventoryStats()que devuelva una cadena:"Products: <count>, Total Value: $<value>"
- Miembros de instancia:
main.cpp: Demuestra cómo los miembros estáticos se comparten entre todos los objetos, mientras que los miembros de instancia siguen siendo únicos. Lee dos nombres de productos y sus precios de la entrada (name1, price1, name2, price2: cada uno en una línea separada), y luego:- Imprime las estadísticas iniciales del inventario usando el nombre de la clase:
Product::getInventoryStats() - Crea un bloque de ámbito con llaves y, dentro de él, crea un
Productcon el primer nombre y precio. Imprime su información y las estadísticas actuales del inventario. - Cuando termine el bloque (y se destruya el primer producto), imprime
"After first scope:"seguido de las estadísticas del inventario - Crea otro
Productcon el segundo nombre y precio. Imprime su información y las estadísticas del inventario. - Crea un tercer
Productcon el nombre"Bonus"y el precio5.00. Imprime las estadísticas finales del inventario.
- Imprime las estadísticas iniciales del inventario usando el nombre de la clase:
Recuerda definir los miembros estáticos fuera de la clase, en el archivo de encabezado (después de la definición de la clase). Da formato a los precios con dos decimales usando std::fixed y std::setprecision(2) de <iomanip>.
Incluye el archivo de encabezado en main.cpp usando #include "Product.h".
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// Lee la entrada
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: Imprime las estadísticas iniciales del inventario usando Product::getInventoryStats()
// TODO: Crea un bloque de ámbito con llaves
// Dentro del bloque:
// - Crea un Product con name1 y price1
// - Imprime su información usando getInfo()
// - Imprime las estadísticas actuales del inventario
// TODO: Después de que termine el bloque, imprime "After first scope:"
// Luego imprime las estadísticas del inventario
// TODO: Crea otro Product con name2 y price2
// Imprime su información y las estadísticas del inventario
// TODO: Crea un tercer Product con el nombre "Bonus" y el precio 5.00
// Imprime las estadísticas finales del inventario
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