Miembros estáticos
Los miembros estáticos pertenecen a la clase en lugar de a instancias específicas, permitiendo compartir datos entre todos los objetos de esa clase. Se almacenan en una zona global o estática con un ciclo de vida que abarca toda la ejecución del programa.
Datos estáticos
Declaración y definición
class Contador {
public:
static int total; // Declaración dentro de la clase
};
int Contador::total = 0; // Definición fuera de la clase
// Desde C++17 se permite inicialización inline
class ContadorModerno {
public:
inline static int cantidad = 0;
};
Características
- Todos los objetos comparten el mismo valor para el miembro estático
- Los miembros estáticos deben definirse fuera de la clase si no son inline
- Pueden ser declarados del mismo tipo que la clase contenedora
Formas de acceso
Contador::total = 5; // Acceso mediante nombre de clase
Contador obj;
obj.total = 10; // Acceso mediante objeto
Contador* ptr = &obj;
ptr->total = 15; // Acceso mediante puntero
Funciones estáticas
class Gestor {
private:
static int contador;
public:
static void mostrarContador() {
cout << "Valor actual: " << contador << endl;
}
static void reiniciar() {
contador = 0;
}
};
int Gestor::contador = 0;
Propiedades clave
- No poseen puntero this, por lo tanto no pueden acceder a miembros no estáticos
- Se invocan directamente desde la clase sin necesidad de crear objetos
- No pueden ser declaradas como const, volatile o virtual
Llamada a funciones estáticas
Gestor::mostrarContador(); // Recomendado
Gestor instancia;
instancia.mostrarContador(); // Válido pero no recomendado
Puntero this
El puntero this es un parámetro implícito en cada función miembro no estática, de tipo constante (Tipo* const), que apunta al objeto que invoca la función. Es un valor de solo lectura generado automáticamente por el compilador.
Usos principales
Resolución de conflictos de nombres
class Persona {
private:
int edad;
public:
void asignarEdad(int edad) {
this->edad = edad; // Diferencia entre parámetro y atributo
}
int obtenerEdad() const {
return this->edad;
}
};
Implementación de encadenamiento
class Operaciones {
private:
int resultado;
public:
Operaciones() : resultado(0) {}
Operaciones& sumar(int valor) {
resultado += valor;
return *this;
}
Operaciones& multiplicar(int factor) {
resultado *= factor;
return *this;
}
int obtenerResultado() const {
return resultado;
}
};
// Uso: encadenamiento de operaciones
Operaciones calc;
int salida = calc.sumar(5).multiplicar(2).sumar(3).obtenerResultado();
Retorno de dirección del objeto
class Elemento {
public:
Elemento* direccion() {
return this;
}
};
Consideraciones importantes
- Las funciones estáticas carecen de this
- En funciones const, this es de tipo const Tipo* const
- Las funciones friend tampoco disponen de this
- No se puede modificar el valor de this
Gestión de memoria dinámica
Los punteros a objetos facilitan la administración dinámica de memoria en el heap, utilizando new/delette para objetos individuales y new[]/delete[] para arreglos, diferenciándose así de las funciones malloc/free de C.
Comparativa entre new y malloc
| Característica | new/delete | malloc/free |
|---|---|---|
| Lenguaje | Operadores de C++ | Funciones de biblioteca C |
| Constructores/Destructores | Ejecutados automáticamente | Solo manejo de memoria |
| Seguridad de tipos | Devuelven tipo específico | Devuelven void* |
| Cálculo de tamaño | Automático | Manual en bytes |
| Sobrecarga | Posible | No posible |
| Manejo de errores | Lanza std::bad_alloc | Devuelve NULL |
Problemas con delete[]
int* arreglo = new int[5]{1,2,3,4,5};
delete[] arreglo;
// Acceder después de liberar produce comportamiento indefinido
for(int i=0; i<5; i++) cout << arreglo[i]; // Peligroso
Razón principal: La memoria ya no pertenece al programa aunque su contenido pueda persistir temporalmente, generando punteros colgantes.
Buenas prácticas
Sintaxis básica
class Registro {
public:
Registro() { cout << "Constructor\n"; }
~Registro() { cout << "Destructor\n"; }
};
Registro* obj = new Registro(); // Un objeto
delete obj;
Registro* lista = new Registro[3]; // Arreglo de objetos
delete[] lista;
Inicialización
int* entero = new int(42);
int* vector = new int[4]{1,2,3};
Punto* punto = new Punto{5,6};
Tratamiento de excepciones
#include <new>
try {
int* grande = new int[999999999999];
} catch(const bad_alloc& error) {
cerr << "Fallo en asignación: " << error.what() << endl;
}
// Alternativa sin excepción
int* seguro = new(nothrow) int[999999999999];
if(!seguro) cerr << "Asignación fallida\n";
Mecanismo friend
Friend permite romper la encapsulación selectivamente, otorgando acceso directo a miembros privados o protegiods a funciones externas o clases completas. Esta relación es unilateral, no transitiva ni heredable.
Funciones friend
Función global friend
class Cuenta {
private:
double saldo;
public:
Cuenta(double monto) : saldo(monto) {}
friend void imprimirSaldo(const Cuenta& cuenta);
};
void imprimirSaldo(const Cuenta& cuenta) {
cout << "Saldo disponible: " << cuenta.saldo << endl;
}
Método de otra clase como friend
class Datos; // Declaración anticipada
class Procesador {
public:
void procesar(Datos& info);
};
class Datos {
private:
int dato;
public:
friend void Procesador::procesar(Datos& info);
};
void Procesador::procesar(Datos& info) {
info.dato = 100;
cout << "Dato modificado: " << info.dato << endl;
}
Clases friend
class Clave; // Anticipada
class Secreto {
private:
int codigo;
public:
Secreto() : codigo(0) {}
friend class Clave;
};
class Clave {
public:
void cambiarCodigo(Secreto& obj, int nuevo) {
obj.codigo = nuevo;
}
int verCodigo(const Secreto& obj) const {
return obj.codigo;
}
};
Puntos importantes
- Unidireccional: Si A es friend de B, no implica que B sea friend de A
- No transitivo: A friend de B y B friend de C no significa A friend de C
- No heredable: Las relaciones friend no se transmiten a clases derivadas
- Uso moderado: Solo cuando sea necesario para mantener cohesión
Ejemplo integrador
#include <iostream>
#include <string>
using namespace std;
class Alumno {
private:
static int matriculados;
int identificador;
string nombre;
public:
Alumno(const string& nom) : nombre(nom) {
this->identificador = ++matriculados;
}
void mostrar() const {
cout << "ID: " << this->identificador << ", Nombre: " << nombre
<< ", Total: " << matriculados << endl;
}
static void limpiar() { matriculados = 0; }
friend void mostrarTotal();
};
int Alumno::matriculados = 0;
void mostrarTotal() {
cout << "Alumnos registrados: " << Alumno::matriculados << endl;
}
int main() {
Alumno a1("Ana"), a2("Carlos");
a1.mostrar();
a2.mostrar();
mostrarTotal();
Alumno::limpiar();
Alumno a3("Beatriz");
a3.mostrar();
return 0;
}