Desarrollo de Clases C++: Herencia, Agregación y Operaciones Numéricas
Clasificado en Informática
Escrito el en español con un tamaño de 13,38 KB
Implementación de Clases y Objetos en C++: Ejemplos Prácticos
Este documento presenta una serie de ejemplos prácticos en C++ para ilustrar conceptos fundamentales de la Programación Orientada a Objetos (POO). Se abordan temas como la definición de clases, la creación de objetos, la encapsulación, la agregación, la herencia y el uso de métodos estáticos. Cada sección incluye un fragmento de código y una breve explicación de los conceptos demostrados.
Nota importante: Para compilar y ejecutar estos ejemplos, es necesario incluir las librerías estándar como <iostream>
y <string>
, y utilizar el espacio de nombres std
(por ejemplo, using namespace std;
). Cada función main()
representa un ejemplo independiente y, en un proyecto real, solo una de ellas debería estar activa a la vez.
1. Sistema Básico de Gestión de Cuentas Bancarias
Este primer ejemplo demuestra cómo modelar un sistema de gestión de cuentas bancarias utilizando clases para representar entidades como Persona, Movimiento y Cuenta. Se ilustran conceptos como la encapsulación de datos y la agregación de objetos (una cuenta contiene movimientos y un titular).
1.1. Clase Persona
Define las propiedades básicas de una persona, como su RUT y nombre, y un método para imprimirlos.
class Persona {
private:
string rut;
string nombre;
public:
Persona(string rut, string nombre) {
this->rut = rut;
this->nombre = nombre;
}
void imprimir() {
cout << "RUT: " << rut << endl;
cout << "Nombre: " << nombre << endl;
}
};
1.2. Clase Movimiento
Representa una transacción bancaria con un folio, fecha y monto. Incluye métodos para acceder a sus atributos y para imprimir la información del movimiento.
class Movimiento {
private:
int folio;
int fecha;
int monto;
public:
Movimiento(int folio, int fecha, int monto) {
this->folio = folio;
this->fecha = fecha;
this->monto = monto;
}
int getFecha() {
return fecha;
}
int getFolio() {
// Corrección: El método getFolio debe retornar el folio, no el monto.
return folio;
}
int getMonto() {
return monto;
}
void imprimir() {
cout << "Folio: " << folio << endl;
cout << "Fecha: " << fecha << endl;
cout << "Monto: " << monto << endl;
}
};
1.3. Clase Cuenta
Gestiona una cuenta bancaria, incluyendo su número, titular, saldo y una lista de movimientos. Demuestra la agregación de objetos (Persona
y Movimiento
) y la lógica de negocio para agregar movimientos y prevenir sobregiros. La lista de movimientos tiene una capacidad máxima de 100.
class Cuenta {
private:
int numero;
Persona *titular;
int saldo;
Movimiento *listaMov[100]; // Arreglo estático para movimientos
int totMov; // Contador de movimientos actuales
public:
Cuenta(int numero, Persona *titular) {
totMov = 0;
saldo = 0;
this->numero = numero;
this->titular = titular;
}
int getSaldo() {
return saldo;
}
bool agregarMovimiento(Movimiento *m) {
bool resultado = true;
if (totMov == 100) {
cout << "No se pueden agregar más movimientos. Límite alcanzado." << endl;
resultado = false;
} else {
// Se asume que la condición incompleta original "<>" buscaba evitar un saldo negativo (sobregiro).
if (saldo + m->getMonto() < 0) {
cout << "No se puede sobregirar. Saldo insuficiente." << endl;
resultado = false;
} else {
saldo = saldo + m->getMonto();
listaMov[totMov] = m;
totMov++;
}
}
return resultado;
}
void imprimir() {
cout << "Número de Cuenta: " << numero << endl;
titular->imprimir();
cout << "Saldo actual: " << saldo << endl;
cout << "--- Lista de Movimientos ---" << endl;
for (int i = 0; i < totMov; ++i) {
listaMov[i]->imprimir();
}
}
};
1.4. Función main
(Ejemplo de Sistema Bancario)
Este bloque de código demuestra la creación de objetos y la interacción entre ellos para simular operaciones básicas de una cuenta bancaria.
main() {
Persona *p = new Persona("1-9", "Perez");
Cuenta *c = new Cuenta(1, p);
Movimiento *m1 = new Movimiento(1, 1, 100);
Movimiento *m2 = new Movimiento(2, 2, 300);
Movimiento *m3 = new Movimiento(3, 2, -100); // Movimiento de retiro
c->agregarMovimiento(m1);
c->agregarMovimiento(m2);
c->agregarMovimiento(m3);
cout << "El saldo final de la cuenta es: " << c->getSaldo() << endl;
cout << endl;
c->imprimir();
// Buena práctica: Liberar la memoria asignada dinámicamente
// delete p;
// delete c; // Esto no libera los movimientos, solo el objeto Cuenta
// delete m1; delete m2; delete m3; // Los movimientos deben ser liberados individualmente si no son gestionados por Cuenta
}
2. Demostración de Herencia en C++
Este segundo ejemplo ilustra el concepto de herencia, donde una clase (Alumno
) deriva de otra (Persona
), reutilizando y extendiendo su funcionalidad. Se observa el uso de miembros protected
y la llamada al método de la clase base.
Nota: La clase Persona
se redefine en este ejemplo con un constructor por defecto y miembros protected
para fines ilustrativos de herencia. En un proyecto real, se evitarían redefiniciones de clases con el mismo nombre.
2.1. Clase Base Persona
(para Herencia)
Versión de la clase Persona
adaptada para ser una clase base, con miembros protected
que permiten el acceso directo a clases derivadas, y un constructor por defecto.
class Persona {
protected:
string rut;
string nombre;
public:
Persona() {} // Constructor por defecto, necesario si no se llama explícitamente a otro constructor
Persona (string rut, string nombre) {
this->rut = rut;
this->nombre = nombre;
}
void imprimir() {
cout << "RUT Persona: " << rut << endl;
cout << "Nombre Persona: " << nombre << endl;
}
void setRut(string rut) {
this->rut = rut;
}
void setNombre(string nombre) {
this->nombre = nombre;
}
};
2.2. Clase Derivada Alumno
La clase Alumno
hereda públicamente de Persona
y añade una propiedad específica (carrera
). Se muestra cómo el constructor de Alumno
puede inicializar los miembros heredados y cómo se puede extender el método imprimir()
de la clase base.
class Alumno: public Persona {
private:
string carrera;
public:
// Constructor de Alumno que inicializa miembros heredados y propios.
// Una alternativa más idiomática sería: Alumno(string rut, string nombre, string carrera) : Persona(rut, nombre) { this->carrera = carrera; }
Alumno (string rut, string nombre, string carrera) {
this->rut = rut; // Acceso directo a miembro protected heredado
this->nombre = nombre; // Acceso directo a miembro protected heredado
this->carrera = carrera;
}
void imprimir() {
Persona::imprimir(); // Llama al método imprimir de la clase base Persona
cout << "Carrera: " << carrera << endl;
}
};
2.3. Función main
(Ejemplo de Herencia)
Demostración de la creación de objetos de la clase base (Persona
) y de la clase derivada (Alumno
), y la invocación de sus respectivos métodos imprimir()
.
main () {
Persona *per = new Persona("1-9", "Pepe");
Alumno *a = new Alumno("3-5", "Jaimito", "Ingeniería Industrial");
a->imprimir();
// Buena práctica: Liberar la memoria
// delete per;
// delete a;
}
3. Operaciones con Números Complejos
Este tercer ejemplo se centra en la representación y manipulación de números complejos. Introduce el concepto de métodos estáticos en la clase Operar
para realizar operaciones matemáticas sin necesidad de instanciar un objeto de esa clase.
3.1. Clase Complejo
Define un número complejo con sus partes real e imaginaria. Incluye métodos para obtener sus componentes y para imprimir el número en el formato estándar (a + bi).
class Complejo {
private:
int real;
int imaginario;
public:
Complejo (int real, int imaginario) {
this->real = real;
this->imaginario = imaginario;
}
int getReal() {
return real;
}
int getImaginario() {
return imaginario;
}
void imprimir() {
cout << real << " + " << imaginario << "i" << endl;
}
};
3.2. Clase Operar
(Métodos Estáticos)
Contiene métodos estáticos para realizar operaciones de suma y multiplicación de números complejos. Los métodos estáticos pueden ser llamados directamente desde la clase (ej. Operar::sumar(...)
) sin crear una instancia de Operar
, ya que no operan sobre el estado de un objeto específico de Operar
.
class Operar {
public:
// Suma dos números complejos y retorna un nuevo objeto Complejo
static Complejo *sumar2(Complejo *a, Complejo *b) {
int r, i;
r = a->getReal() + b->getReal();
i = a->getImaginario() + b->getImaginario();
Complejo *c = new Complejo(r, i);
return c;
}
// Suma dos números complejos e imprime el resultado directamente
static void sumar(Complejo *a, Complejo *b) {
int r, i;
r = a->getReal() + b->getReal();
i = a->getImaginario() + b->getImaginario();
Complejo *c = new Complejo(r, i);
c->imprimir();
// delete c; // Si 'c' se crea aquí, debería liberarse aquí si no se retorna.
}
// Multiplica dos números complejos y retorna un nuevo objeto Complejo
static Complejo *mult(Complejo *a, Complejo *b) {
int r, i;
// Fórmula de multiplicación de complejos: (a+bi)*(c+di) = (ac-bd) + (ad+bc)i
r = a->getReal() * b->getReal() - a->getImaginario() * b->getImaginario();
i = a->getReal() * b->getImaginario() + a->getImaginario() * b->getReal();
Complejo *c = new Complejo(r, i);
return c;
}
// Multiplica tres números complejos (versión 1: usando variables intermedias)
static Complejo *mult3v1(Complejo *a, Complejo *b, Complejo *c) {
Complejo *hh = mult(a, b);
Complejo *tt = mult(hh, c);
// delete hh; // Liberar memoria intermedia si no se necesita más
return tt;
}
// Multiplica tres números complejos (versión 2: anidada)
static Complejo *mult3v2(Complejo *a, Complejo *b, Complejo *c) {
return mult(mult(a,b), c);
}
};
3.3. Función main
(Ejemplo de Números Complejos)
Este bloque de código demuestra la creación de objetos Complejo
y el uso de los métodos estáticos de la clase Operar
para realizar sumas y multiplicaciones.
main() {
Complejo *uno = new Complejo(1, 2);
Complejo *dos = new Complejo(3, 4);
Complejo *tres = new Complejo(5, 6);
cout << "Números complejos iniciales:" << endl;
uno->imprimir();
dos->imprimir();
tres->imprimir();
cout << endl;
cout << "Suma de uno y dos (impresión directa desde el método):" << endl;
Operar::sumar(uno, dos);
cout << endl;
cout << "Multiplicación de uno y dos (retorna objeto):" << endl;
Complejo *r = Operar::mult(uno, dos);
r->imprimir();
cout << endl;
cout << "Multiplicación de tres números (uno, dos, tres) usando mult3v2:" << endl;
Complejo *r3 = Operar::mult3v2(uno, dos, tres);
r3->imprimir();
cout << endl;
// Es crucial liberar la memoria asignada dinámicamente para evitar fugas de memoria.
// delete uno;
// delete dos;
// delete tres;
// delete r;
// delete r3;
}
Estos ejemplos proporcionan una base sólida para comprender y aplicar los principios de la Programación Orientada a Objetos en C++. La práctica con estos conceptos es fundamental para el desarrollo de software robusto y mantenible.