Saltar a contenido

Programacion orientada a objetos (POO) en C++

Objetivo

Tener una base clara de POO para despues entender mejor:

  • diseño de clases en C++,
  • como se traducen a bajo nivel,
  • y como reconocer esos patrones en reversing.

Que es un objeto

Un objeto es una entidad con:

  • estado (atributos/datos),
  • comportamiento (metodos).

Ejemplo: Perro

  • estado: nombre, peso, edad, esCachorro
  • comportamiento: ladrar(), comer(), mostrarInfo()

En C++, una clase define ese tipo y un objeto es una instancia concreta.


Clase vs objeto

class Animal {
public:
    std::string nombre;
    float peso;
    void sonar() const {
        std::cout << "Sonido generico\n";
    }
};

int main() {
    Animal perro; // objeto
    Animal gato;  // otro objeto
}
  • Animal es el tipo.
  • perro y gato son objetos distintos de ese tipo.

Encapsulacion

La encapsulacion protege los datos internos y controla su acceso.

Regla practica:

  • atributos: private o protected,
  • interfaz publica: metodos public.
class Animal {
private:
    std::string nombre;
    float peso;

public:
    Animal(const std::string& n, float p) : nombre(n), peso(p) {}

    std::string getNombre() const { return nombre; }
    float getPeso() const { return peso; }

    void setPeso(float p) {
        if (p >= 0.0f) peso = p; // validacion minima
    }
};

Constructores

Sirven para inicializar objetos en una sola linea:

Animal perro("Rocky", 12.5f);

Punto tecnico importante

En C++, los constructores no se sobreescriben y no participan en polimorfismo. En herencia, la clase derivada llama al constructor base por lista de inicializacion:

class Perro : public Animal {
private:
    bool cachorro;

public:
    Perro(const std::string& n, float p, bool c)
        : Animal(n, p), cachorro(c) {}
};

Herencia

Permite reutilizar lo comun en una clase base y especializar en clases derivadas.

class Animal {
protected:
    std::string nombre;
public:
    Animal(const std::string& n) : nombre(n) {}
};

class Gato : public Animal {
public:
    Gato(const std::string& n) : Animal(n) {}
};

Modificadores y herencia (resumen)

  • herencia public: mantiene visibilidad publica/protegida del padre.
  • herencia protected: miembros publicos del padre pasan a protegidos.
  • herencia private: miembros publicos/protegidos del padre pasan a privados.

private del padre nunca es accesible directo desde la hija.


Polimorfismo

Un mismo mensaje (metodo) produce comportamiento distinto segun el tipo real del objeto.

Se logra con virtual en la base y override en la derivada.

class Animal {
public:
    virtual ~Animal() = default;
    virtual void sonido() const {
        std::cout << "Sonido generico\n";
    }
};

class Perro : public Animal {
public:
    void sonido() const override {
        std::cout << "Guau\n";
    }
};

Punto tecnico importante

override aplica a metodos virtuales, no a constructores.


Abstraccion

La abstraccion se enfoca en lo esencial y define contratos.

Clase abstracta con metodo virtual puro:

class Animal {
public:
    virtual ~Animal() = default;
    virtual void sonido() const = 0; // contrato
};
  • No puedes instanciar Animal.
  • Toda clase derivada concreta debe implementar sonido().

Ejemplo completo: lista heterogenea de animales

Version moderna (recomendada) con std::unique_ptr:

#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Animal {
public:
    virtual ~Animal() = default;
    virtual void sonido() const = 0;
};

class Perro : public Animal {
public:
    void sonido() const override { std::cout << "Guau\n"; }
};

class Gato : public Animal {
public:
    void sonido() const override { std::cout << "Miau\n"; }
};

int main() {
    std::vector<std::unique_ptr<Animal>> animales;
    animales.push_back(std::make_unique<Perro>());
    animales.push_back(std::make_unique<Gato>());

    for (const auto& a : animales) {
        a->sonido();
    }
}

Definiciones teoricas (resumen extendido)

1) Objeto

Un objeto es cualquier entidad que quieras modelar en tu programa como tipo de dato:

  • fisica: celular, auto, mascota
  • virtual: ticket digital, turno medico, orden de compra

Todo objeto combina:

  • estado (atributos),
  • comportamiento (metodos).

2) Clase

Una clase es el molde/tipo que define:

  • que datos tiene el objeto,
  • que operaciones puede ejecutar.

Al crear variables de ese tipo, creas instancias (objetos).

3) Atributo y metodo

  • atributo: dato interno del objeto (ej: peso)
  • metodo: funcion asociada al objeto (ej: sonido())

4) Instanciacion

Declarar un objeto se parece a declarar una variable:

Animal perro;

Animal es el tipo, perro es la instancia.

5) Constructor

Metodo especial de inicializacion que corre al crear el objeto.

  • no tiene tipo de retorno,
  • se llama igual que la clase.

Sirve para evitar bloques largos de asignaciones despues de crear el objeto.

6) Encapsulacion

Encapsular es proteger el estado interno y exponer solo la interfaz necesaria.

Objetivo:

  • evitar cambios invalidos desde fuera,
  • centralizar validaciones,
  • mantener invariantes del objeto.

7) Getters y setters

Permiten acceso controlado a atributos no publicos.

Convencion comun:

  • getAtributo()
  • setAtributo(valor)

Un setter puede incluir reglas de negocio (ej: no permitir peso negativo).

8) Modificadores de acceso

  • public: accesible desde cualquier parte.
  • private: accesible solo dentro de la clase.
  • protected: accesible dentro de la clase y sus derivadas.

Nota: en class, si no especificas acceso, el default es private.

9) Herencia

Permite reutilizar comportamiento comun en una clase base y especializar en clases hijas.

La hija puede:

  • agregar atributos/metodos,
  • redefinir metodos virtuales,
  • usar miembros public/protected heredados.

10) Tipo de herencia y restriccion de acceso

Heredar con public, protected o private no hace "mas publico" nada; solo mantiene o restringe:

  • herencia public: conserva visibilidad heredada (public sigue public)
  • herencia protected: lo public del padre pasa a protected
  • herencia private: lo public/protected del padre pasa a private

private del padre nunca es accesible directo desde la hija.

11) Polimorfismo

Misma llamada, distinto comportamiento segun tipo real del objeto.

En C++ clasico:

  • base con metodo virtual,
  • derivada con override.

Regla clave: override aplica a metodos virtuales, no a constructores.

12) Abstraccion

Quedarte con lo esencial del dominio y modelar contratos.

En C++, una clase abstracta se crea con metodos virtuales puros (= 0) y:

  • no puede instanciarse,
  • obliga a derivadas concretas a implementar el contrato.

13) Colecciones polimorficas

Para mezclar Perro, Gato, etc. en un mismo contenedor:

  • el contenedor debe ser de tipo base (Animal* o smart pointer a Animal),
  • las llamadas virtuales se resuelven segun tipo dinamico.

14) Memoria dinamica en este contexto

Con punteros crudos:

  • new requiere delete,
  • riesgo de leaks/double free si no hay disciplina.

Con C++ moderno:

  • preferir std::unique_ptr y std::make_unique.

15) Correcciones tecnicas del guion (importante)

  • Decir "el constructor se sobreescribe" es incorrecto: los constructores no son virtuales.
  • Lo correcto es: la clase derivada define su propio constructor y llama al constructor base en la lista de inicializacion.
  • override correcto se escribe override (no overridge).


Relacion con reversing (pista rapida)

Cuando hay polimorfismo en C++ suelen aparecer:

  • tablas virtuales (vtable),
  • punteros a vtable dentro del objeto,
  • llamadas indirectas a metodos virtuales.

Cuando no hay virtuales, normalmente veras llamadas directas a metodos concretos.


Checklist practico de POO en C++

  • Usa atributos private o protected.
  • Expon solo metodos necesarios en public.
  • Inicializa con constructores claros.
  • Para herencia, inicializa la base en lista de inicializacion.
  • Para polimorfismo, usa virtual + override.
  • Si usas punteros, prefiere smart pointers (unique_ptr) a new/delete manual.

Errores comunes

  • Confundir constructor con metodo virtual.
  • Intentar acceder desde hija a miembros private del padre.
  • Guardar objetos derivados por valor en contenedores de base (object slicing).
  • Usar new sin estrategia de liberacion.