Proyectos Prácticos. Después de haber recorrido los conceptos fundamentales de C y C++, este capítulo tiene como objetivo aplicar todos esos conocimientos en proyectos completos. Estos proyectos no solo servirán como ejercicios de consolidación, sino que también ofrecerán una visión más clara de cómo se integran múltiples elementos del lenguaje.
Cada proyecto está diseñado para abordar aspectos específicos:
- Entrada/salida de datos
- Estructuras y punteros
- Memoria dinámica
- Archivos
- Programación orientada a objetos
- Estructuras de datos dinámicas
- Uso de la STL
Empezamos…
14.1 Agenda de Contactos en C
Objetivo
Vamos a crear un programa en C que permita al usuario almacenar, buscar, listar y eliminar contactos. Cada contacto tendrá información básica como nombre, teléfono y correo electrónico. Utilizaremos estructuras, archivos y funciones para organizar el código de forma modular.
Estructura de los datos
Usaremos una estructura (struct
) para representar cada contacto:
typedef struct {
char nombre[50];
char telefono[20];
char email[50];
} Contacto;
Funcionalidades del programa
Imprimiremos un menú con las siguientes funciones:
- Añadir nuevo contacto
- Listar todos los contactos
- Buscar contacto por nombre
- Eliminar contacto
- Salir del programa
Crearemos una función para cada una de las opciones del menú.
Todos los datos los guardaremos en un archivo de texto plano (agenda.txt
).
Código fuente del proyecto
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARCHIVO "agenda.txt"
typedef struct {
char nombre[50];
char telefono[20];
char email[50];
} Contacto;
void agregarContacto() {
FILE *f = fopen(ARCHIVO, "a");
Contacto c;
printf("Nombre: ");
fgets(c.nombre, sizeof(c.nombre), stdin);
strtok(c.nombre, "\n");
printf("Teléfono: ");
fgets(c.telefono, sizeof(c.telefono), stdin);
strtok(c.telefono, "\n");
printf("Email: ");
fgets(c.email, sizeof(c.email), stdin);
strtok(c.email, "\n");
fwrite(&c, sizeof(Contacto), 1, f);
fclose(f);
printf("Contacto agregado.\n");
}
void listarContactos() {
FILE *f = fopen(ARCHIVO, "r");
Contacto c;
int i = 1;
if (f == NULL) {
printf("No hay contactos aún.\n");
return;
}
while (fread(&c, sizeof(Contacto), 1, f)) {
printf("\nContacto %d:\n", i++);
printf("Nombre: %s\n", c.nombre);
printf("Teléfono: %s\n", c.telefono);
printf("Email: %s\n", c.email);
}
fclose(f);
}
void buscarContacto() {
FILE *f = fopen(ARCHIVO, "r");
Contacto c;
char nombreBuscar[50];
int encontrado = 0;
if (f == NULL) {
printf("No hay contactos aún.\n");
return;
}
printf("Nombre a buscar: ");
fgets(nombreBuscar, sizeof(nombreBuscar), stdin);
strtok(nombreBuscar, "\n");
while (fread(&c, sizeof(Contacto), 1, f)) {
if (strcmp(c.nombre, nombreBuscar) == 0) {
printf("Contacto encontrado:\n");
printf("Nombre: %s\n", c.nombre);
printf("Teléfono: %s\n", c.telefono);
printf("Email: %s\n", c.email);
encontrado = 1;
break;
}
}
if (!encontrado)
printf("Contacto no encontrado.\n");
fclose(f);
}
void eliminarContacto() {
FILE *f = fopen(ARCHIVO, "r");
FILE *temp = fopen("temp.txt", "w");
Contacto c;
char nombreEliminar[50];
int eliminado = 0;
if (f == NULL) {
printf("No hay contactos.\n");
return;
}
printf("Nombre del contacto a eliminar: ");
fgets(nombreEliminar, sizeof(nombreEliminar), stdin);
strtok(nombreEliminar, "\n");
while (fread(&c, sizeof(Contacto), 1, f)) {
if (strcmp(c.nombre, nombreEliminar) != 0) {
fwrite(&c, sizeof(Contacto), 1, temp);
} else {
eliminado = 1;
}
}
fclose(f);
fclose(temp);
remove(ARCHIVO);
rename("temp.txt", ARCHIVO);
if (eliminado)
printf("Contacto eliminado correctamente.\n");
else
printf("Contacto no encontrado.\n");
}
int main() {
int opcion;
do {
printf("\n--- AGENDA DE CONTACTOS ---\n");
printf("1. Agregar contacto\n");
printf("2. Listar contactos\n");
printf("3. Buscar contacto\n");
printf("4. Eliminar contacto\n");
printf("5. Salir\n");
printf("Seleccione una opción: ");
scanf("%d", &opcion);
getchar(); // limpiar buffer
switch (opcion) {
case 1: agregarContacto(); break;
case 2: listarContactos(); break;
case 3: buscarContacto(); break;
case 4: eliminarContacto(); break;
case 5: printf("Saliendo...\n"); break;
default: printf("Opción no válida.\n");
}
} while (opcion != 5);
return 0;
}
Explicación del funcionamiento
- Utilizamos la estructura
Contacto
para representar los contactos. - Los datos se escriben y leen desde un archivo usando
fwrite()
yfread()
en modo binario. - Utilizamos funciones como
fgets()
,strtok()
ystrcmp()
para el manejo de cadenas. - Para eliminar un contacto copiamos todos los contactos a un archivo temporal excepto el contacto que vamos a eliminar, y luego lo renombramos para dejar el nombre del archivo original.
- El menú permite al usuario interactuar con todas las funciones repetitivamente hasta que elige la opción salir.
14.2 Calculadora Científica en C++
Objetivo
Diseñar una calculadora científica en C++ que permita realizar operaciones matemáticas utilizando el paradigma de programación orientada a objetos junto con bibliotecas estándar como cmath
.
La calculadora permitirá realizar las siguiente operaciones:
- Operaciones básicas: suma, resta, multiplicación y división
- Funciones avanzadas: potencia, raíz cuadrada, logaritmo, funciones trigonométricas
- Menú interactivo para seleccionar la operación deseada
Estructura del programa
Usaremos una clase Calculadora
que contendrá métodos para cada operación. El menú se controlará desde la función main
.
Código fuente
#include <iostream>
#include <cmath>
using namespace std;
class Calculadora {
public:
// Operaciones básicas
double sumar(double a, double b) {
return a + b;
}
double restar(double a, double b) {
return a - b;
}
double multiplicar(double a, double b) {
return a * b;
}
double dividir(double a, double b) {
if (b == 0) {
cout << "Error: división por cero.\n";
return NAN;
}
return a / b;
}
// Operaciones científicas
double potencia(double base, double exponente) {
return pow(base, exponente);
}
double raizCuadrada(double x) {
if (x < 0) {
cout << "Error: raíz de número negativo.\n";
return NAN;
}
return sqrt(x);
}
double logaritmo(double x) {
if (x <= 0) {
cout << "Error: logaritmo de valor no positivo.\n";
return NAN;
}
return log(x);
}
double seno(double x) {
return sin(x);
}
double coseno(double x) {
return cos(x);
}
double tangente(double x) {
return tan(x);
}
};
int main() {
Calculadora calc;
int opcion;
double a, b;
do {
cout << "\n--- CALCULADORA CIENTÍFICA ---\n";
cout << "1. Sumar\n";
cout << "2. Restar\n";
cout << "3. Multiplicar\n";
cout << "4. Dividir\n";
cout << "5. Potencia\n";
cout << "6. Raíz Cuadrada\n";
cout << "7. Logaritmo\n";
cout << "8. Seno\n";
cout << "9. Coseno\n";
cout << "10. Tangente\n";
cout << "0. Salir\n";
cout << "Seleccione una opción: ";
cin >> opcion;
switch (opcion) {
case 1:
cout << "Ingrese dos números: ";
cin >> a >> b;
cout << "Resultado: " << calc.sumar(a, b) << endl;
break;
case 2:
cout << "Ingrese dos números: ";
cin >> a >> b;
cout << "Resultado: " << calc.restar(a, b) << endl;
break;
case 3:
cout << "Ingrese dos números: ";
cin >> a >> b;
cout << "Resultado: " << calc.multiplicar(a, b) << endl;
break;
case 4:
cout << "Ingrese dos números: ";
cin >> a >> b;
cout << "Resultado: " << calc.dividir(a, b) << endl;
break;
case 5:
cout << "Base y exponente: ";
cin >> a >> b;
cout << "Resultado: " << calc.potencia(a, b) << endl;
break;
case 6:
cout << "Número: ";
cin >> a;
cout << "Resultado: " << calc.raizCuadrada(a) << endl;
break;
case 7:
cout << "Número: ";
cin >> a;
cout << "Resultado: " << calc.logaritmo(a) << endl;
break;
case 8:
cout << "Ángulo en radianes: ";
cin >> a;
cout << "Seno: " << calc.seno(a) << endl;
break;
case 9:
cout << "Ángulo en radianes: ";
cin >> a;
cout << "Coseno: " << calc.coseno(a) << endl;
break;
case 10:
cout << "Ángulo en radianes: ";
cin >> a;
cout << "Tangente: " << calc.tangente(a) << endl;
break;
case 0:
cout << "Saliendo...\n";
break;
default:
cout << "Opción no válida.\n";
}
} while (opcion != 0);
return 0;
}
Explicación
- Empleamos una clase
Calculadora
para encapsular las operaciones. - Para las operaciones avanzadas utilizamos funciones de la biblioteca
<cmath>
. - El menú interactivo permite probar todas las funciones sin reiniciar el programa.
- Manejamos casos de error como división por cero o logaritmo de número no positivo.
- Esperamos a que el usuario ingrese ángulos en radianes para las funciones trigonométricas.
14.3 Sistema de Gestión de Estudiantes con Archivos
Objetivo
Este proyecto es similar al primero, vamos a desarrollar un sistema en C que permita:
- Registrar estudiantes con nombre, ID y nota.
- Consultar el listado de estudiantes guardados.
- Almacenar y recuperar la información usando archivos de texto.
- Aplicar lo aprendido sobre estructuras, manejo de archivos y menús.
Estructura del proyecto
El programa permitirá realizar varias operaciones:
- Añadir un nuevo estudiante
- Mostrar todos los estudiantes registrados
- Buscar un estudiante por ID
Usaremos un archivo estudiantes.txt
para guardar los datos en texto plano, y una estructura struct Estudiante
para organizar la información.
Código fuente
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NOMBRE 100
typedef struct {
int id;
char nombre[MAX_NOMBRE];
float nota;
} Estudiante;
void agregarEstudiante() {
FILE *archivo = fopen("estudiantes.txt", "a");
if (archivo == NULL) {
perror("No se pudo abrir el archivo");
return;
}
Estudiante e;
printf("ID del estudiante: ");
scanf("%d", &e.id);
getchar(); // Limpiar el buffer
printf("Nombre del estudiante: ");
fgets(e.nombre, MAX_NOMBRE, stdin);
e.nombre[strcspn(e.nombre, "\n")] = 0; // Eliminar salto de línea
printf("Nota final: ");
scanf("%f", &e.nota);
fprintf(archivo, "%d,%s,%.2f\n", e.id, e.nombre, e.nota);
fclose(archivo);
printf("Estudiante agregado con éxito.\n");
}
void mostrarEstudiantes() {
FILE *archivo = fopen("estudiantes.txt", "r");
if (archivo == NULL) {
printf("No hay estudiantes registrados.\n");
return;
}
Estudiante e;
char linea[150];
printf("\n--- Lista de Estudiantes ---\n");
while (fgets(linea, sizeof(linea), archivo)) {
sscanf(linea, "%d,%[^,],%f", &e.id, e.nombre, &e.nota);
printf("ID: %d | Nombre: %s | Nota: %.2f\n", e.id, e.nombre, e.nota);
}
fclose(archivo);
}
void buscarPorID() {
FILE *archivo = fopen("estudiantes.txt", "r");
if (archivo == NULL) {
printf("Archivo no encontrado.\n");
return;
}
int idBuscado;
printf("Ingrese ID a buscar: ");
scanf("%d", &idBuscado);
Estudiante e;
char linea[150];
int encontrado = 0;
while (fgets(linea, sizeof(linea), archivo)) {
sscanf(linea, "%d,%[^,],%f", &e.id, e.nombre, &e.nota);
if (e.id == idBuscado) {
printf("Estudiante encontrado: %s con nota %.2f\n", e.nombre, e.nota);
encontrado = 1;
break;
}
}
if (!encontrado)
printf("Estudiante no encontrado.\n");
fclose(archivo);
}
int main() {
int opcion;
do {
printf("\n--- MENÚ DE GESTIÓN DE ESTUDIANTES ---\n");
printf("1. Agregar estudiante\n");
printf("2. Mostrar todos los estudiantes\n");
printf("3. Buscar estudiante por ID\n");
printf("0. Salir\n");
printf("Seleccione una opción: ");
scanf("%d", &opcion);
getchar(); // Limpieza de buffer
switch (opcion) {
case 1: agregarEstudiante(); break;
case 2: mostrarEstudiantes(); break;
case 3: buscarPorID(); break;
case 0: printf("Saliendo...\n"); break;
default: printf("Opción inválida.\n");
}
} while (opcion != 0);
return 0;
}
Explicación
- Los datos los almacenamos en formato CSV con la estructura (
ID,nombre,nota
) en un archivo de texto. - Usamos
sscanf
para extraer los datos desde cada línea del archivo. - Las operaciones las organizamos en funciones separadas para facilitar la gestión del código.
- Implementamos búsqueda por ID para mostrar solo un estudiante específico.
14.4 Simulación de la Cola de un Banco con Listas Dinámicas
Objetivo
Controlar la manipulación de datos mediante listas dinámicas, para ello vamos a simular el funcionamiento de una cola en un banco, donde los clientes llegan, esperan su turno, y son atendidos en orden. Este proyecto permite comprender:
- Estructuras dinámicas (listas enlazadas)
- Gestión de memoria dinámica (
malloc
,free
) - Uso de estructuras (
struct
) y punteros - Operaciones típicas de una cola (FIFO): insertar al final, eliminar al principio
Descripción
El programa tendrá las siguientes funcionalidades:
- Agregar un cliente a la cola
- Atender (eliminar) al primer cliente de la cola
- Mostrar la cola actual
- Salir del programa
Cada cliente tendrá un número de turno y un nombre.
Código
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NOMBRE 50
typedef struct Cliente {
int turno;
char nombre[MAX_NOMBRE];
struct Cliente *siguiente;
} Cliente;
Cliente *inicio = NULL;
Cliente *fin = NULL;
int contadorTurno = 1;
// Añadir cliente a la cola
void agregarCliente() {
Cliente *nuevo = (Cliente *)malloc(sizeof(Cliente));
if (!nuevo) {
printf("No se pudo asignar memoria.\n");
return;
}
nuevo->turno = contadorTurno++;
printf("Ingrese nombre del cliente: ");
getchar(); // limpiar buffer
fgets(nuevo->nombre, MAX_NOMBRE, stdin);
nuevo->nombre[strcspn(nuevo->nombre, "\n")] = '\0'; // eliminar salto de línea
nuevo->siguiente = NULL;
// Si la lista está vacía al crear un elemento nuevo será el primero,
// Si no actualizamos el puntero del último elemento al nuevo que hemos creado
// y el nuevo elemento se convierte en el último de la cola.
if (fin == NULL) {
inicio = fin = nuevo;
} else {
fin->siguiente = nuevo;
fin = nuevo;
}
printf("Cliente agregado. Turno: %d\n", nuevo->turno);
}
// Atender al primer cliente
void atenderCliente() {
if (inicio == NULL) {
printf("No hay clientes en la cola.\n");
return;
}
Cliente *atendido = inicio;
printf("Atendiendo a %s (Turno %d)\n", atendido->nombre, atendido->turno);
inicio = inicio->siguiente;
if (inicio == NULL)
fin = NULL;
free(atendido);
}
// Mostrar todos los clientes en la cola
void mostrarCola() {
if (inicio == NULL) {
printf("La cola está vacía.\n");
return;
}
Cliente *actual = inicio;
printf("\n--- Clientes en la cola ---\n");
while (actual != NULL) {
printf("Turno %d - %s\n", actual->turno, actual->nombre);
actual = actual->siguiente;
}
}
int main() {
int opcion;
do {
printf("\n--- MENÚ DEL BANCO ---\n");
printf("1. Agregar cliente a la cola\n");
printf("2. Atender cliente\n");
printf("3. Mostrar cola\n");
printf("0. Salir\n");
printf("Opción: ");
scanf("%d", &opcion);
switch (opcion) {
case 1: agregarCliente(); break;
case 2: atenderCliente(); break;
case 3: mostrarCola(); break;
case 0: printf("Cerrando simulador...\n"); break;
default: printf("Opción inválida.\n");
}
} while (opcion != 0);
return 0;
}
Explicación
- Utilizamos una lista enlazada simple para representar la cola.
- Cada nuevo cliente se añade al final de la lista (operación típica de cola).
- Al atender un cliente, se elimina el primero de la lista.
- Se maneja cuidadosamente la memoria con
malloc
yfree
para evitar fugas. - El número de turno se genera automáticamente con un contador global.
14.5 Juego de Adivinanza con Objetos en C++
Objetivo
Desarrollar un juego simple de adivinanza numérica donde el jugador debe adivinar un número secreto generado aleatoriamente.
Este proyecto refuerza conceptos como:
- Uso de clases y objetos
- Encapsulamiento de lógica en métodos
- Generación de números aleatorios
- Ciclos y condiciones
Requisitos funcionales
- El programa genera un número secreto entre 1 y 100.
- El jugador tiene intentos ilimitados hasta adivinarlo.
- El programa da pistas: “Muy alto” o “Muy bajo”.
- El jugador puede jugar otra vez al finalizar.
Código
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class JuegoAdivinanza {
private:
int numeroSecreto;
int intentos;
public:
JuegoAdivinanza() {
// Semilla para números aleatorios
srand(time(0));
numeroSecreto = rand() % 100 + 1; // entre 1 y 100
intentos = 0;
}
void jugar() {
int intento;
cout << "¡Bienvenido al juego de adivinanza!\n";
cout << "Adivina un número entre 1 y 100.\n";
do {
cout << "Ingresa tu intento: ";
cin >> intento;
intentos++;
if (intento > numeroSecreto) {
cout << "Demasiado alto. Intenta de nuevo.\n";
} else if (intento < numeroSecreto) {
cout << "Demasiado bajo. Intenta de nuevo.\n";
} else {
cout << "¡Felicidades! Adivinaste el número en " << intentos << " intentos.\n";
}
} while (intento != numeroSecreto);
}
void reiniciar() {
numeroSecreto = rand() % 100 + 1;
intentos = 0;
}
};
int main() {
JuegoAdivinanza juego;
char opcion;
do {
juego.jugar();
cout << "\n¿Quieres jugar de nuevo? (s/n): ";
cin >> opcion;
if (opcion == 's' || opcion == 'S') {
juego.reiniciar();
}
} while (opcion == 's' || opcion == 'S');
cout << "¡Gracias por jugar!\n";
return 0;
}
Explicación
- La clase
JuegoAdivinanza
encapsula los atributos y métodos necesarios. - El número secreto se genera en el constructor usando
rand() % 100 + 1
. - El método
jugar()
contiene la lógica del juego: bucle de intento y retroalimentación. - El método
reiniciar()
permite generar un nuevo número secreto para volver a jugar sin salir del programa.
Resumen del Capítulo
En este capítulo hemos aplicado los conocimientos adquiridos a lo largo del tutorial mediante cinco proyectos completos. Cada uno está diseñado para asentar conceptos de C y C++ como son las estructuras, archivos, programación orientada a objetos, estructuras dinámica etc.
- Agenda de Contactos en C – Repasamo estructuras, archivos y menús interactivos.
- Calculadora Científica en C++ – Aplicamos clases, métodos y uso de librerías matemáticas.
- Sistema de Gestión de Estudiantes con Archivos – Trabajamos con archivos, estructuras y validación de datos.
- Simulación de Cola de Banco con Listas Dinámicas – Profundizamos en listas enlazadas y simulación de eventos.
- Juego de Adivinanza con Objetos en C++ – Practicamos orientación a objetos, ciclos y generación aleatoria.
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)