Miembros estáticos, puntero this, memoria dinámica y mecanismo de friend en C++

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;
}

Etiquetas: C++ Programación Orientada a Objetos miembros estáticos puntero this Memoria Dinámica

Publicado el 8-24 16:03