Programación Orientada a Objetos en C++. La Programación Orientada a Objetos (POO) es uno de los pilares fundamentales del lenguaje C++. A diferencia del lenguaje C, que se basa en la programación estructurada, C++ introduce mecanismos para organizar y estructurar el código en torno a objetos: entidades que combinan datos (atributos) y comportamiento (métodos o funciones miembro).
Este paradigma permite representar conceptos del mundo real de forma natural, modelando entidades como clases, donde actúan las caracteríasticas de herencia, polimorfismo, constructores y destructores, así como reglas para controlar el acceso a los datos internos mediante encapsulamiento.
A lo largo de este capítulo los aspectos para:
- Definir clases y objetos, y cómo se relacionan entre sí.
- Crear constructores para inicializar objetos automáticamente, y destructores para limpiar recursos.
- Aplicar encapsulamiento usando los modificadores de acceso
private
,public
yprotected
. - Utilizar la herencia para extender clases y el polimorfismo para redefinir comportamiento.
- Sobrecargar operadores como
+
o==
para que funcionen con tus propias clases. - Declarar y usar métodos estáticos y constantes.
- Introducirte en la programación genérica usando plantillas (
template
) para escribir código reutilizable y flexible.
Como siempre, esto es un tutorial que te ayudará a introducirte en materia, si quieres profundizar en la Programación Orientada a Objetos, te puedo aconsejar la lectura del libro: «Programación Orientada a Objetos con C++. 4ª de Fco. Javier Ceballos Sierra y Antonio García Tome, Editorial Ra-Ma»
Empezamos!!!!!
11.1. Clases y Objetos.
¿Qué es una clase?
Una clase es un modelo o plantilla que describe cómo se construyen los objetos. Contiene atributos (variables de datos) que determinan el estado de un objeto y métodos (funciones) que realizan acciones sobre el objeto.
En términos simples, una clase define qué puede tener y qué puede hacer un objeto. No ocupa memoria por sí sola, sino que sirve para crear objetos (instancias). Básicamente es una «descripción» de un tipo de objeto.
¿Qué es un objeto?
Un objeto es una instancia concreta de una clase. Cuando se crea un objeto, el sistema reserva memoria para los datos que define la clase, y se puede acceder a sus métodos y atributos.
Estructura de una clase en C++
Para definir una clase, debemos indicar sus atributos y métodos de la siguiente manera:
class NombreDeLaClase {
public:
// Atributos
tipo atributo1;
tipo atributo2;
// Métodos
void metodo1();
tipo metodo2(tipo parametro);
};
public
: indica que los miembros son accesibles desde fuera de la clase.- También existen
private
yprotected
, que veremos más adelante.
Veamos un Ejemplo básico
#include <iostream>
using namespace std;
// Definición de la clase Persona
class Persona {
public:
// Atributos
string nombre;
int edad;
// Métodos
void mostrarInformacion() {
cout << "Nombre: " << nombre << endl;
cout << "Edad: " << edad << " años" << endl;
}
};
int main() {
// Creación de un objeto de tipo Persona
Persona p1;
// Asignación de valores a los atributos
p1.nombre = "Lucía";
p1.edad = 25;
// Llamada a un método
p1.mostrarInformacion();
return 0;
}
Si nos fijamos en el código:
- Se define la clase
Persona
con dos atributos (nombre
yedad
) y un método llamadomostrarInformacion()
. - En el
main()
, se crea un objetop1
de tipoPersona
(igual que hacemos como si fuera una variable) - Se asignan valores a los atributos del objeto.
- Se llama al método del objeto para mostrar su información por pantalla.
Cuando se declara Persona p1;
, el compilador reserva espacio en memoria para almacenar una variable nombre
y una variable edad
dentro del objeto p1
.
Al escribir p1.nombre = "Lucía";
, estás accediendo directamente al atributo del objeto.
Si te fijas, para el acceso a los miembros de un objeto se realiza mediante el operador punto (.
):
p1.edad = 30; // Asignación
cout << p1.nombre; // Lectura
p1.mostrarInformacion(); // Llamada a método
Clases como tipos de datos
Una vez definida una clase, podemos tratarla como un nuevo tipo de dato personalizado. De hecho, podríamos crearnos tantos objetos como necesitemos.
Persona p2, p3;
p2.nombre = "Carlos";
p3.nombre = "Ana";
Cada objeto tiene su propia copia de los atributos y puede usar los métodos definidos en la clase.
Buenas prácticas
- Por convención, los nombres de las clases comienzan con mayúscula.
- Se suele declarar primero la clase y luego su uso en
main()
u otras funciones. - En programas más grandes, las clases se definen en archivos
.h
(cabecera) y se implementan en archivos.cpp
.
Ventajas de usar clases y objetos
La Programación Orientada a Objetos proporciona muchas ventajas
- Modularidad: cada clase puede manejar un aspecto distinto del programa.
- Reutilización: las clases se pueden reutilizar en diferentes programas o proyectos.
- Mantenibilidad: el código se organiza mejor, facilitando los cambios y pruebas.
- Representación natural: los objetos permiten modelar elementos del mundo real como personas, coches, televisores, etc.
11.2 Constructores y Destructores
¿Qué es un Constructor?
Un constructor es un método especial que se ejecuta automáticamente cuando se crea un objeto nuevo de una clase. Su objetivo es inicializar los atributos del objeto.
Características de los Constructores:
- Tienen el mismo nombre que la clase.
- No devuelven ningún valor, ni siquiera
void
. - Pueden tener parámetros (constructores parametrizados).
- Puede haber más de un constructor en una clase (sobrecarga de constructores).
- Si no defines ninguno, el compilador crea un constructor por defecto vacío.
Veamos un ejemplo de constructor por defecto
#include <iostream>
using namespace std;
class Persona {
public:
string nombre;
int edad;
// Constructor por defecto
Persona() {
nombre = "Sin nombre";
edad = 0;
}
void mostrarInformacion() {
cout << "Nombre: " << nombre << ", Edad: " << edad << endl;
}
};
int main() {
Persona p1;
p1.mostrarInformacion(); // Muestra: Nombre: Sin nombre, Edad: 0
return 0;
}
El constructor se ejecuta automáticamente al crear el objeto p1
, y asigna valores iniciales, en este caso «sin nombre» y 0.
Constructores con parámetros
Permiten crear objetos con valores iniciales diferentes desde el momento de su declaración.
class Persona {
public:
string nombre;
int edad;
// Constructor parametrizado
Persona(string n, int e) {
nombre = n;
edad = e;
}
void mostrarInformacion() {
cout << "Nombre: " << nombre << ", Edad: " << edad << endl;
}
};
int main() {
Persona p1("Lucía", 28);
Persona p2("Carlos", 35);
p1.mostrarInformacion(); // Lucía, 28
p2.mostrarInformacion(); // Carlos, 35
return 0;
}
Al ser un constructor una función, cuando definimos el objeto debemos facilitar entre () el valor que vamos a asignar al objeto. En este caso definimos dos objetos y les asignamos el nombre de «Lucía» y 28 años, y «Carlos» de 35 años.
Sobrecarga de Constructores
Podemos tener varios constructores con diferente número y tipo de parámetros:
class Persona {
public:
string nombre;
int edad;
// Constructor por defecto
Persona() {
nombre = "Sin nombre";
edad = 0;
}
// Constructor con un parámetro
Persona(string n) {
nombre = n;
edad = 0;
}
// Constructor con dos parámetros
Persona(string n, int e) {
nombre = n;
edad = e;
}
void mostrarInformacion() {
cout << "Nombre: " << nombre << ", Edad: " << edad << endl;
}
};
int main() {
Persona p1;
Persona p2("Juan");
Persona p3("Juan", 23);
}
¿Qué es un Destructor?
Un destructor es un método especial que se ejecuta automáticamente cuando un objeto sale de alcance o es eliminado. Su propósito es liberar recursos, como memoria dinámica o archivos abiertos.
Los destructores tienen las siguientes características:
- Se llaman igual que la clase, pero con el símbolo
~
delante. - No reciben parámetros.
- No devuelven valor.
- Solo puede haber un destructor por clase.
- El compilador lo llama automáticamente (no hace falta invocarlo).
Veamos un ejemplo de Destructor:
#include <iostream>
using namespace std;
class Persona {
public:
string nombre;
Persona(string n) {
nombre = n;
cout << "Constructor llamado para: " << nombre << endl;
}
~Persona() {
cout << "Destructor llamado para: " << nombre << endl;
}
};
int main() {
Persona p1("Ana");
{
Persona p2("Luis");
} // Aquí se destruye p2
return 0;
}
El programa actuará el programa liberando los objetos de memoria.
Constructor llamado para: Ana
Constructor llamado para: Luis
Destructor llamado para: Luis
Destructor llamado para: Ana
Observa que p2
se destruye al finalizar el bloque {
} (el ámbito donde se definió el objeto) y p1
al salir del main()
.
Es muy importante definir destructores, primcipalmente cuando se usan recursos como:
- Memoria reservada con
new
omalloc
- Archivos abiertos
- Conexiones a bases de datos o redes
- etc.
es importante liberarlos automáticamente en el destructor para evitar fugas de memoria o errores.
11.3 Encapsulamiento
¿Qué es el Encapsulamiento?
El encapsulamiento es uno de los pilares fundamentales de la Programación Orientada a Objetos (POO). Consiste en ocultar los detalles internos de la implementación de una clase y exponer solo lo necesario a través de una interfaz pública.
En otras palabras, el objetivo del encapsulamiento es proteger los datos y asegurar que solo se acceda a ellos de forma controlada.
¿Cómo se implementa en C++?
C++ permite definir el nivel de acceso a los atributos y métodos mediante los modificadores de acceso:
Modificador | Significado |
---|---|
private | Accesible solo desde dentro de la clase. |
public | Accesible desde cualquier parte del programa. |
protected | Accesible desde la clase y sus subclases. |
Ejemplode encapsulamiento
#include <iostream>
using namespace std;
class CuentaBancaria {
private:
double saldo; // atributo privado
public:
CuentaBancaria() {
saldo = 0.0;
}
void depositar(double cantidad) {
if (cantidad > 0) {
saldo += cantidad;
}
}
void retirar(double cantidad) {
if (cantidad > 0 && cantidad <= saldo) {
saldo -= cantidad;
}
}
double obtenerSaldo() {
return saldo;
}
};
int main() {
CuentaBancaria cuenta;
cuenta.depositar(1000);
cuenta.retirar(200);
cout << "Saldo actual: $" << cuenta.obtenerSaldo() << endl;
return 0;
}
- El atributo
saldo
es privado, así que no se puede modificar directamente desdemain()
. - Solo se puede acceder a él mediante las funciones públicas
depositar
,retirar
yobtenerSaldo
.
El Encapsulamiento nos aporta los beneficios de:
- Seguridad: Evita el acceso no autorizado o accidental a los datos sensibles.
- Control: Puedes definir reglas específicas para modificar los datos.
- Mantenibilidad: Facilita cambios internos sin afectar al resto del código.
- Reutilización: Las clases bien encapsuladas son más fáciles de usar en diferentes contextos.
Es importante seguir estas pautas:
- Siempre que sea posible, declarar los atributos como
private
oprotected
. - Crear métodos
get
yset
si necesitas permitir acceso controlado a esos atributos. - Evitar hacer pública la lógica interna de la clase.
Ejemplo con getters y setters
En la POO y con la finalidad de implementar el encapsulamiento, se hace uso de lo que denominamos setters y getters para el manejo de los datos de un objeto. La idea es proporcionar métodos SET para establecer valores a atributos y GET para recuperarlos. Veamos como se hace.
class Persona {
private:
string nombre;
int edad;
public:
void setNombre(string n) {
nombre = n;
}
string getNombre() {
return nombre;
}
void setEdad(int e) {
if (e > 0)
edad = e;
}
int getEdad() {
return edad;
}
};
11.4 Herencia y Polimorfismo
¿Qué es la Herencia?
La herencia es un mecanismo de la Programación Orientada a Objetos (POO) que permite a una clase heredar atributos y métodos de otra clase.
Esto permite reutilizar código (y no tener que volver a escribirlo) y establecer relaciones jerárquicas entre clases.
En C++, una clase puede heredar de otra mediante el operador :
seguido de un modo de herencia (public
, protected
o private
).
Sintaxis
class ClaseBase {
// atributos y métodos
};
class ClaseDerivada : public ClaseBase {
// nuevos atributos y métodos, o sobreescritura
};
Veamoslo mejor con un ejemplo
Edit#include <iostream>
using namespace std;
class Animal {
public:
void comer() {
cout << "Este animal está comiendo." << endl;
}
};
class Perro : public Animal {
public:
void ladrar() {
cout << "El perro está ladrando." << endl;
}
};
int main() {
Perro miPerro;
miPerro.comer(); // heredado
miPerro.ladrar(); // propio
return 0;
}
Como puedes ver en el ejemplo, la clase Perro
hereda de Animal
, por lo que puede usar el método comer()
sin volver a implementarlo.
Tipos de herencia en C++
Tipo | Descripción |
---|---|
public | Los miembros públicos y protegidos de la clase base mantienen su visibilidad. |
protected | Los miembros públicos y protegidos se convierten en protected en la clase derivada. |
private | Los miembros públicos y protegidos se convierten en private . |
🔹
¿Qué es el Polimorfismo?
El polimorfismo significa que una misma operación puede comportarse de diferente manera en distintas clases.
En C++, el polimorfismo se logra principalmente mediante funciones virtuales y herencia.
Ejemplo de polimorfismo
#include <iostream>
using namespace std;
class Animal {
public:
virtual void hacerSonido() {
cout << "Sonido genérico de animal." << endl;
}
};
class Gato : public Animal {
public:
void hacerSonido() override {
cout << "Miau" << endl;
}
};
class Perro : public Animal {
public:
void hacerSonido() override {
cout << "Guau" << endl;
}
};
void reproducirSonido(Animal* a) {
a->hacerSonido();
}
int main() {
Gato g;
Perro p;
reproducirSonido(&g); // Miau
reproducirSonido(&p); // Guau
return 0;
}
En este ejemplo
hacerSonido()
es una función virtual en la clase baseAnimal
.- Cada clase derivada puede sobrescribir esa función.
- Usamos un puntero a
Animal
para llamar a la función apropiada en cada objeto real: esto es polimorfismo dinámico.
¿Por que utilizamos la herencia y polimorfismo? Pues porque nos aporta:
Reutilización de código: Evitas repetir lo mismo en varias clases.
Extensibilidad: Puedes crear nuevas clases derivadas sin alterar el código existente.
Flexibilidad: Permite escribir funciones genéricas que pueden operar con cualquier clase hija.
Organización jerárquica: Refleja relaciones naturales entre entidades (Ej: Animal
→ Perro
, Gato
, etc.)
11.5 Sobrecarga de Operadores
¿Qué es la sobrecarga de operadores?
En C++, la sobrecarga de operadores permite redefinir el comportamiento de los operadores estándar (+
, -
, ==
, <<
, etc.) para que funcionen con objetos definidos por el usuario (clases), de una forma intuitiva y natural.
Esto es útil cuando queremos que nuestras clases se comporten de forma similar a los tipos nativos.
Veamos como hacerlo. Supongamos que tenemos una clase Punto
para representar coordenadas (x, y). Sería muy útil poder sumar dos puntos usando el operador +
.
Sintaxis:
TipoRetorno operatorOperador(Parametros) {
// implementación
}
Por ejemplo, para sobrecargar el operador +
lo definiríamos de la siguiente manera:
Punto operator+(const Punto& otro);
Viendolo en un ejemplo quedaría así:
#include <iostream>
using namespace std;
class Punto {
private:
int x, y;
public:
Punto(int x = 0, int y = 0) : x(x), y(y) {}
// Sobrecarga del operador +
Punto operator+(const Punto& p) {
return Punto(x + p.x, y + p.y);
}
void mostrar() const {
cout << "(" << x << ", " << y << ")" << endl;
}
};
int main() {
Punto p1(2, 3);
Punto p2(4, 5);
Punto suma = p1 + p2; // Uso del operador sobrecargado
suma.mostrar(); // Imprime: (6, 8)
return 0;
}
- Se define el operador
+
para que sume los valores dex
ey
de dos objetosPunto
.
- La llamada
p1 + p2
es en realidad una llamada ap1.operator+(p2)
.
¿Qué operadores que se pueden sobrecargar?
- Aritméticos:
+
,-
,*
,/
,%
- Comparación:
==
,!=
,<
,>
,<=
,>=
- Asignación:
=
,+=
,-=
, etc. - Entrada/salida:
<<
,>>
- Acceso:
[]
,()
,->
,*
, etc.
¿Qué Operadores No se pueden sobrecargar?
::
(resolución de ámbito).
(acceso a miembro).*
(puntero a miembro)sizeof
,typeid
,alignof
Sobrecarga de operadores de comparación
Ya hemos visto como sobrecargar un operador aritmético, veamos como hacerlo para los operadores de comparación
bool operator==(const Punto& p) {
return x == p.x && y == p.y;
}
Operadores como funciones friend
A veces, necesitamos acceso a miembros privados de la clase. En esos casos, podemos definir el operador como función amiga (friend
), lo haríamos de la siguiente manera:
#include <iostream>
using namespace std;
class Punto {
private:
int x, y;
public:
Punto(int x = 0, int y = 0) : x(x), y(y) {}
friend ostream& operator<<(ostream& os, const Punto& p);
};
ostream& operator<<(ostream& os, const Punto& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
int main() {
Punto p1(1, 2);
cout << "Punto: " << p1 << endl;
return 0;
}
- Sobrecargamos el operador
<<
para que podamos imprimir el objetoPunto
directamente concout
.
@Tip. Recomendaciones
✔ Usa const
cuando el operador no modifica el objeto.
✔ Usa referencias para evitar copias innecesarias.
✔ Mantén el comportamiento de los operadores coherente con sus significados tradicionales.
11.6 Métodos estáticos y constantes
En C++, los métodos estáticos y constantes permiten establecer comportamientos distintos y más seguros en el diseño de clases. Ambos conceptos ayudan a estructurar mejor el código y a controlar el acceso a los datos.
Métodos estáticos (static
)
Un método estático pertenece a la clase y no a un objeto concreto.
Esto significa que se puede llamar al método sin necesidad de crear una instancia de la clase.
Características:
- No puede acceder directamente a miembros no estáticos.
- Puede acceder a miembros estáticos de la clase.
- Se declara con la palabra clave
static
.
Por ejemplo:
#include <iostream>
using namespace std;
class Contador {
private:
static int cuenta; // Atributo estático común a todos los objetos
public:
Contador() {
cuenta++;
}
static void mostrarCuenta() {
cout << "Objetos creados: " << cuenta << endl;
}
};
// Definición del miembro estático fuera de la clase
int Contador::cuenta = 0;
int main() {
Contador c1, c2, c3;
Contador::mostrarCuenta(); // Se puede llamar sin instancia
return 0;
}
cuenta
es un atributo compartido por todos los objetos de la clase.mostrarCuenta
es un método estático que accede al atributo estático.- Se puede llamar usando el nombre de la clase.
Métodos constantes (const
)
Un método constante es aquel que no modifica el estado del objeto.
Esto garantiza que el método no cambiará ningún atributo del objeto.
Características:
- Se declara añadiendo
const
al final de la cabecera del método. - Solo puede llamar a otros métodos
const
. - No puede modificar atributos del objeto (salvo que sean
mutable
).
Ejemplo:
#include <iostream>
using namespace std;
class Punto {
private:
int x, y;
public:
Punto(int x, int y) : x(x), y(y) {}
void mostrar() const {
cout << "(" << x << ", " << y << ")" << endl;
}
// Este método produciría un error si intentara modificar x o y
// void mover() const { x++; } // ❌ ERROR
};
int main() {
const Punto p(3, 4);
p.mostrar(); // ✅ Puede llamarse porque es const
return 0;
}
- El método
mostrar()
está marcado comoconst
, por lo que puede usarse con objetos constantes. - Si intentáramos modificar
x
oy
dentro del método, el compilador lo impediría.
Combinando static
y const
Es posible tener atributos estáticos constantes, típicos para definir valores constantes compartidos:
class Config {
public:
static const int MAX_USUARIOS = 100;
};
¿Qué beneficios nos aporta su uso?
Concepto | Beneficio principal |
---|---|
static | Permite compartir valores y funciones sin instancias |
const | Aumenta la seguridad del código (no modifica estado) |
11.7 Introducción a la programación genérica (plantillas)
La programación genérica en C++ permite escribir funciones y clases que funcionan con cualquier tipo de dato sin necesidad de duplicar código para cada tipo.
Esto se logra mediante el uso de plantillas (templates), una de las características más potentes y distintivas del lenguaje C++.
¿Qué es una plantilla?
Una plantilla es un mecanismo que permite definir funciones o clases en términos de tipos genéricos.
El compilador genera las versiones concretas de la función o clase cuando son utilizadas con tipos específicos.
🔸 Plantillas de función
La forma básica es:
template <typename T>
T mayor(T a, T b) {
return (a > b) ? a : b;
}
Veamos como usarlo con un ejemplo:
#include <iostream>
using namespace std;
template <typename T>
T mayor(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << mayor(5, 10) << endl; // int
cout << mayor(3.14, 2.71) << endl; // double
cout << mayor('a', 'z') << endl; // char
return 0;
}
- La función
mayor
es genérica: acepta dos valores del mismo tipoT
y devuelve el mayor. - El compilador genera automáticamente versiones de la función para
int
,double
, ychar
según su uso.
Plantillas de clase
Del mismo modo, podemos definir clases genéricas:
template <typename T>
class Caja {
private:
T valor;
public:
Caja(T v) : valor(v) {}
T obtener() { return valor; }
};
Ejemplo:
#include <iostream>
using namespace std;
template <typename T>
class Caja {
private:
T valor;
public:
Caja(T v) : valor(v) {}
T obtener() { return valor; }
};
int main() {
Caja<int> c1(100);
Caja<string> c2("Hola");
cout << c1.obtener() << endl;
cout << c2.obtener() << endl;
return 0;
}
Caja<int>
crea una instancia de la clase para enteros.Caja<string>
para cadenas.- Ambas utilizan la misma definición de clase, lo que reduce duplicación de código.
¿Qué ventajas tienen las plantillas?
Ventaja | Descripción |
---|---|
Reutilización de código | No es necesario repetir código para diferentes tipos. |
Seguridad en tiempo de compilación | El compilador verifica los tipos al generar las versiones necesarias. |
Flexibilidad | Se pueden combinar con otras características del lenguaje, como clases, herencia, etc. |
Consideraciones a tener en cuenta:
- Las plantillas se definen y se implementan generalmente en el mismo archivo (.h o .hpp), ya que el compilador necesita el código fuente para generar instancias.
- No deben usarse si se requiere polimorfismo en tiempo de ejecución (en su lugar se usarán punteros y clases base virtuales).
Aquí termina este capítulo donde nos hemos introducido a los fundamentos de la Programación Orientada a Objetos (POO) en C++. Comenzamos con la definición y uso de clases y objetos para pasar posteriormente a los constructores y los destructores que permiten inicializar y limpiar los objetos. Estudiamos el principio de encapsulamiento para proteger los datos, y analizamos la herencia y el polimorfismo como mecanismos para la reutilización del código. Aprendimos cómo sobrecargar operadores para personalizar comportamientos y cómo declarar métodos estáticos y constantes. Finalmente, hemos visto una pequeña introducción a las plantillas (templates) para escribir código genérico y reutilizable.
Ejercicio 1: Clase CuentaBancaria
con métodos básicos
Enunciado:
Crea una clase CuentaBancaria
que contenga el nombre del titular, el número de cuenta y el saldo. Implementa métodos para ingresar dinero, retirar dinero (solo si hay saldo suficiente), y mostrar los datos de la cuenta.
CuentaBancaria
con métodos básicosEnunciado:
Crea una clase
CuentaBancaria
que contenga el nombre del titular, el número de cuenta y el saldo. Implementa métodos para ingresar dinero, retirar dinero (solo si hay saldo suficiente), y mostrar los datos de la cuenta.Código:
#include <iostream>
#include <string>
using namespace std;
class CuentaBancaria {
private:
string titular;
string numeroCuenta;
double saldo;
public:
CuentaBancaria(string t, string n, double s) : titular(t), numeroCuenta(n), saldo(s) {}
void ingresar(double cantidad) {
saldo += cantidad;
}
void retirar(double cantidad) {
if (cantidad <= saldo) {
saldo -= cantidad;
} else {
cout << "Fondos insuficientes.\n";
}
}
void mostrarDatos() const {
cout << "Titular: " << titular << endl;
cout << "Número de Cuenta: " << numeroCuenta << endl;
cout << "Saldo: " << saldo << " EUR" << endl;
}
};
int main() {
CuentaBancaria cuenta("Laura García", "ES123456789", 1000.0);
cuenta.mostrarDatos();
cuenta.ingresar(250.0);
cuenta.retirar(100.0);
cuenta.retirar(2000.0); // intento fallido
cout << "\nDespués de operaciones:\n";
cuenta.mostrarDatos();
return 0;
}
Explicación:
- Definimos una clase que encapsula los datos de una cuenta bancaria.
- Proporcionamos métodos para modificar y consultar su estado.
- Controlamos los retiros para no permitir saldos negativos, usando una verificación con condicional.
Ejercicio 2: Clase Rectángulo
con sobrecarga del operador ==
Enunciado:
Define una clase Rectangulo
que tenga atributos alto
y ancho
. Crea una sobrecarga del operador ==
para comparar si dos rectángulos son exactamente iguales (mismo alto y ancho).
Rectángulo
con sobrecarga del operador ==
Enunciado:
Define una clase
Rectangulo
que tenga atributos alto
y ancho
. Crea una sobrecarga del operador ==
para comparar si dos rectángulos son exactamente iguales (mismo alto y ancho).Código:
#include <iostream>
using namespace std;
class Rectangulo {
private:
int alto, ancho;
public:
Rectangulo(int a, int b) : alto(a), ancho(b) {}
bool operator==(const Rectangulo& otro) const {
return alto == otro.alto && ancho == otro.ancho;
}
void mostrar() const {
cout << "Rectángulo (" << alto << " x " << ancho << ")\n";
}
};
int main() {
Rectangulo r1(5, 10);
Rectangulo r2(5, 10);
Rectangulo r3(6, 8);
r1.mostrar();
r2.mostrar();
r3.mostrar();
cout << "¿r1 == r2? " << (r1 == r2 ? "Sí" : "No") << endl;
cout << "¿r1 == r3? " << (r1 == r3 ? "Sí" : "No") << endl;
return 0;
}
Explicación:
- La sobrecarga del operador
==
permite comparar objetosRectangulo
como si fueran tipos primitivos. - El método devuelve
true
si tantoalto
comoancho
coinciden.
Ejercicio 3: Uso de template
para una clase genérica Caja
Enunciado:
Crea una clase genérica Caja
que pueda contener cualquier tipo de valor (int, float, string, etc.). Implementa un método mostrarContenido()
que imprima el contenido guardado.
template
para una clase genérica Caja
Enunciado:
Crea una clase genérica
Caja
que pueda contener cualquier tipo de valor (int, float, string, etc.). Implementa un método mostrarContenido()
que imprima el contenido guardado.Código:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Caja {
private:
T contenido;
public:
Caja(T valor) : contenido(valor) {}
void mostrarContenido() const {
cout << "Contenido: " << contenido << endl;
}
};
int main() {
Caja<int> cajaEnteros(42);
Caja<string> cajaTexto("Hola Mundo");
Caja<double> cajaDecimal(3.14);
cajaEnteros.mostrarContenido();
cajaTexto.mostrarContenido();
cajaDecimal.mostrarContenido();
return 0;
}
Explicación:
Como hemos visto en el último apartado del capítulo, el uso de plantillas (template
) permite definir clases genéricas. En este caso, Caja<T>
puede almacenar y mostrar cualquier tipo de dato.
Bibliografía del tutorial de C/C++.
- C/C++. Curso de programación. Autor: Miguel Angel Acera (Editorial: Anaya Multimedia)
- C/C++. Curso de programación. Autor: Francisco José Ceballos (Editorial: RA-MA)
- Un recorrido por C++. Autor Bjarne Stroustrup (Editorial: Anaya Multimedia)
- 115 Ejercicios resueltos de programación C++. Autor Jorge Fernando Betancourt e Inma Yolanda Polanco (Editorial: RA-MA)