Saltar al contenido
Portada » Lenguajes » 11. Programación Orientada a Objetos en C++

11. Programación Orientada a Objetos en C++

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 y protected.
  • 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 y protected, 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:

  1. Se define la clase Persona con dos atributos (nombre y edad) y un método llamado mostrarInformacion().
  2. En el main(), se crea un objeto p1 de tipo Persona (igual que hacemos como si fuera una variable)
  3. Se asignan valores a los atributos del objeto.
  4. 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 o malloc
  • 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:

ModificadorSignificado
privateAccesible solo desde dentro de la clase.
publicAccesible desde cualquier parte del programa.
protectedAccesible 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 desde main().
  • Solo se puede acceder a él mediante las funciones públicas depositar, retirar y obtenerSaldo.

El Encapsulamiento nos aporta los beneficios de:

  1. Seguridad: Evita el acceso no autorizado o accidental a los datos sensibles.
  2. Control: Puedes definir reglas específicas para modificar los datos.
  3. Mantenibilidad: Facilita cambios internos sin afectar al resto del código.
  4. 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 o protected.
  • Crear métodos get y set 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++

TipoDescripción
publicLos miembros públicos y protegidos de la clase base mantienen su visibilidad.
protectedLos miembros públicos y protegidos se convierten en protected en la clase derivada.
privateLos 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 base Animal.
  • 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: AnimalPerro, 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 de x e y de dos objetos Punto.
  • La llamada p1 + p2 es en realidad una llamada a p1.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 objeto Punto directamente con cout.

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 como const, por lo que puede usarse con objetos constantes.
  • Si intentáramos modificar x o y 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?

ConceptoBeneficio principal
staticPermite compartir valores y funciones sin instancias
constAumenta 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 tipo T y devuelve el mayor.
  • El compilador genera automáticamente versiones de la función para int, double, y char 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?

VentajaDescripción
Reutilización de códigoNo es necesario repetir código para diferentes tipos.
Seguridad en tiempo de compilaciónEl compilador verifica los tipos al generar las versiones necesarias.
FlexibilidadSe 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.

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).

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 objetos Rectangulo como si fueran tipos primitivos.
  • El método devuelve true si tanto alto como ancho 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.

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.

Logo C++

Bibliografía del tutorial de C/C++.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *