2.10 · Modularidad: Scope, Paso por Valor/Referencia y Multi-archivo
🎯 Objetivo de aprendizaje
Al terminar este tema vas a poder:
- Explicar qué es el scope (alcance) de variables en C++: local, global, de bloque.
- Pasar argumentos por valor y entender que se hace una copia.
- Pasar argumentos por referencia con & y modificar el valor original.
- Pasar argumentos por puntero con * (introducción).
- Organizar un programa en múltiples archivos .cpp y .h.
- Usar prototipos en archivos de cabecera.
- Compilar proyectos multi-archivo con g++ archivo1.cpp archivo2.cpp -o programa.
La modularidad es lo que diferencia a un programa de "tarea" de un programa "profesional". Con módulos, tu código se vuelve mantenible, testeable y reutilizable. Es la base de cualquier proyecto serio.
🧠 Teoría y conceptos — 2.10.1 — El scope de variables: local, global, de bloque

Los 3 tipos de scope en C++
Ejemplo: los 3 scopes
#include <iostream>
using namespace std;
int globalVar = 100; // GLOBAL
void miFuncion() {
int localVar = 50; // LOCAL
cout << "Global: " << globalVar << endl; // OK
cout << "Local: " << localVar << endl; // OK
// cout << bloqueVar; // ❌ No existe aquí
}
int main() {
int mainVar = 10; // LOCAL a main
if (true) {
int bloqueVar = 5; // DE BLOQUE
cout << "Global: " << globalVar << endl; // OK
cout << "Main: " << mainVar << endl; // OK
cout << "Bloque: " << bloqueVar << endl; // OK
}
// cout << bloqueVar; // ❌ No existe aquí
cout << "Main: " << mainVar << endl; // OK
miFuncion();
return 0;
}
Diagrama de scopes anidados
┌────────────────────────┐
│ GLOBAL │
│ (todo el programa) │
│ globalVar = 100 │
│ │
│ ┌──────────────┐ │
│ │ MAIN │ │
│ │ mainVar=10 │ │
│ │ │ │
│ │ ┌────────┐ │ │
│ │ │ if {} │ │ │
│ │ │bloqueV │ │ │
│ │ │=5 │ │ │
│ │ └────────┘ │ │
│ │ │ │
│ │ ┌──────────┐│ │
│ │ │miFuncion ││ │
│ │ │localV=50 ││ │
│ │ └──────────┘│ │
│ └──────────────┘ │
└────────────────────────┘
Lo que muestra: cada bloque "contiene" a los más internos. Un bloque interno puede ver las variables de los bloques externos, pero NO al revés. Esto se llama regla del scope anidado.
Variables con el mismo nombre en scopes diferentes
int x = 10; // Global
void foo() {
int x = 5; // Local, OCULTA a la global
cout << x; // 5
}
int main() {
cout << x; // 10 (la global)
foo();
return 0;
}
La regla: la variable local "tapa" a la global. Para acceder a la global explícitamente, usa el operador :::
int x = 10; // Global
void foo() {
int x = 5; // Local
cout << ::x; // 10 (la global, accedida con ::)
cout << x; // 5 (la local)
}
los 3 tipos de scope con un ejemplo propio. Memorízalos: local, global, de bloque.
2.10.2 — Paso por valor: la copia segura

Recordatorio
Ya vimos esto en el Tema 2.9. Por defecto, en C++ los argumentos se pasan por valor: la función recibe una COPIA, modificar el parámetro NO afecta al original.
void incrementar(int x) {
x++; // Solo modifica la copia
}
int main() {
int n = 5;
incrementar(n);
cout << n; // 5, NO 6
}
Cuándo usar paso por valor
✅ Cuando la función no necesita modificar el argumento. ✅ Para tipos pequeños (int, double, char, bool). ✅ Por defecto, si no estás seguro.
por qué el paso por valor es "seguro". Da un ejemplo de cuándo es problemático.
2.10.3 — Paso por referencia con &: modificar el original

El concepto
Si quieres que la función modifique el valor original, pasas el argumento por referencia con &. La función trabaja directamente sobre la variable original, no una copia.
void incrementar(int& x) { // & indica paso por referencia
x++; // Modifica la variable original
}
int main() {
int n = 5;
incrementar(n);
cout << n; // 6, NO 5
}
Ejemplo: intercambiar dos valores
void intercambiar(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 3;
cout << "Antes: x=" << x << ", y=" << y << endl; // x=5, y=3
intercambiar(x, y);
cout << "Después: x=" << x << ", y=" << y << endl; // x=3, y=5
}
Sin referencias, esto sería imposible.
Ejemplo: leer múltiples valores con validación
void leerEdad(int& edad) {
do {
cout << "Edad (0-150): ";
cin >> edad;
} while (edad < 0 || edad > 150);
}
int main() {
int edad;
leerEdad(edad); // Por referencia: modifica `edad` directamente
cout << "Edad válida: " << edad << endl;
}
Paso por referencia const (solo lectura, sin copia)
A veces quieres eficiencia de referencia (no copia) pero no necesitas modificar. Usa const:
void imprimir(const string& texto) { // No copia, no modifica
cout << texto << endl;
}
int main() {
string s = "Hola mundo";
imprimir(s); // Eficiente (sin copiar el string completo)
}
const le dice al compilador "prometo no modificar esto", y si intentas, da error.
la diferencia entre paso por valor, por referencia (`&`) y por referencia constante (`const &`). Da un ejemplo de cada uno.
2.10.4 — Paso por puntero con *: alternativa a la referencia

El concepto
Un puntero es una variable que almacena la dirección de memoria de otra variable. En lugar de pasar la variable, pasas su dirección.
void incrementar(int* x) { // * indica que x es un puntero a int
(*x)++; // Desreferenciar: acceder al valor en esa dirección
}
int main() {
int n = 5;
incrementar(&n); // &n es la dirección de n
cout << n; // 6
}
Comparación: puntero vs referencia
En C++ moderno, prefiere referencias sobre punteros (más seguro). Pero en el Capítulo 3 veremos punteros porque son la base de los arreglos dinámicos y la memoria.
Cuándo usar punteros
✅ Cuando necesitas una variable que puede no apuntar a nada (nullptr). ✅ Cuando interactúas con código C legacy. ✅ Cuando trabajas con memoria dinámica (lo veremos en el Cap 3).
las 3 formas de paso (valor, referencia, puntero) y cuándo usar cada una.
2.10.5 — Cuándo usar cada tipo de paso

Regla de decisión
¿La función necesita MODIFICAR el argumento?
├── NO
│ ├── ¿El argumento es un tipo grande (string, struct)?
│ │ ├── SÍ → const referencia (eficiencia)
│ │ └── NO → valor (simpleza)
│ └──
└── SÍ
├── ¿El argumento PUEDE ser null?
│ ├── SÍ → puntero
│ └── NO → referencia
Tabla resumen
2.10.6 — Archivos de cabecera (.h): declarar funciones

El problema
En el Tema 2.9 vimos los prototipos. En programas con muchas funciones, tener todos los prototipos en el mismo archivo .cpp se vuelve inmanejable.
La solución: archivos .h
Un archivo .h (header) contiene declaraciones (prototipos, constantes, tipos) que otros archivos pueden incluir.
// math_utils.h — Declaraciones
#ifndef MATH_UTILS_H // Guardas de inclusión
#define MATH_UTILS_H
int sumar(int a, int b);
int restar(int a, int b);
const double PI = 3.14159;
#endif
Las guardas de inclusión (#ifndef, #define, #endif) evitan que el archivo se incluya múltiples veces, lo que causaría errores de "declaración duplicada".
Uso en otro archivo
// main.cpp — Implementación + uso
#include <iostream>
#include "math_utils.h" // Incluir nuestro header
using namespace std;
// Implementaciones de las funciones declaradas en math_utils.h
int sumar(int a, int b) { return a + b; }
int restar(int a, int b) { return a - b; }
int main() {
cout << sumar(3, 5) << endl; // 8
cout << restar(10, 3) << endl; // 7
return 0;
}
la estructura de un archivo `.h` con guardas de inclusión. Memorízala.
2.10.7 — Multi-archivo: separar .h y .cpp
Estructura profesional
mi_proyecto/
├── main.cpp
├── math_utils.h
├── math_utils.cpp
├── string_utils.h
└── string_utils.cpp
Ejemplo: proyecto "Calculadora" multi-archivo
math_utils.h:
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int sumar(int a, int b);
int restar(int a, int b);
int multiplicar(int a, int b);
double dividir(double a, double b);
#endif
math_utils.cpp:
#include "math_utils.h"
int sumar(int a, int b) { return a + b; }
int restar(int a, int b) { return a - b; }
int multiplicar(int a, int b) { return a * b; }
double dividir(double a, double b) {
if (b == 0) return 0; // O lanzar error
return a / b;
}
main.cpp:
#include <iostream>
#include "math_utils.h"
using namespace std;
int main() {
cout << "5 + 3 = " << sumar(5, 3) << endl;
cout << "10 - 4 = " << restar(10, 4) << endl;
cout << "6 * 7 = " << multiplicar(6, 7) << endl;
cout << "15 / 4 = " << dividir(15, 4) << endl;
return 0;
}
Ventajas del multi-archivo
la estructura de un proyecto multi-archivo. Dibuja el diagrama de dependencias.
2.10.8 — Compilar proyectos multi-archivo
Compilar con g++
g++ main.cpp math_utils.cpp -o programa.exe
El comando le dice a g++: "compila main.cpp y math_utils.cpp, vincúlalos en un solo ejecutable llamado programa.exe".
El proceso
- g++ compila cada .cpp por separado a un archivo objeto (.o).
- El linker une los archivos objeto y las librerías.
- Genera el ejecutable final.
Errores comunes
Error: "undefined reference to sumar"
Si declaraste sumar en math_utils.h pero no la implementaste en math_utils.cpp (o no incluiste math_utils.cpp en la compilación), el linker no encuentra la definición.
Solución: asegúrate de:
- Implementar la función en el .cpp.
- Incluir el .cpp en la línea de compilación.
Error: "redefinition of sumar"
Si implementaste sumar en math_utils.cpp Y en main.cpp (olvidaste las guardas de inclusión).
Solución: implementa cada función en un solo .cpp, y en los demás solo pon el prototipo (en el .h).
Script de compilación (Makefile básico)
# Makefile
programa: main.o math_utils.o
g++ main.o math_utils.o -o programa
main.o: main.cpp math_utils.h
g++ -c main.cpp
math_utils.o: math_utils.cpp math_utils.h
g++ -c math_utils.cpp
clean:
rm *.o programa
Para compilar: make. Para limpiar: make clean.
el comando de compilación multi-archivo. Memorízalo: `g++ archivo1.cpp archivo2.cpp -o programa`.
🤖 IA como copiloto — 🤖 AI Mission 2.10
Prompt 1: "Te voy a pegar un programa multi-archivo en C++ que tiene errores de compilación. NO me los corrijas. Solo dime: (1) qué errores ves, (2) qué archivos faltan o están mal, (3) cómo solucionarlo. Máximo 150 palabras."
Prompt 2: "Tengo un programa con 3 archivos: main.cpp, utils.h, utils.cpp. Dame el comando exacto para compilarlos todos juntos en un ejecutable llamado app.exe. Explícame qué hace cada parte del comando. Máximo 100 palabras."
🧪 Laboratorio — 🧪 LAB 2.10 — 6 ejercicios de modularidad
🛠️ Herramienta recomendada para los diagramas de flujo de este LAB: Para los ejercicios que pidan un diagrama de flujo, usa [draw.io (diagrams.net)](https://app.diagrams.net/) — herramienta gratis, sin registro, que funciona en el navegador. Tiene plantillas con la simbología ANSI/ISO lista (en `General → Flowchart`). Exporta tus diagramas como PNG e insértalos en tu cuaderno digital o entrégalos junto con el código.
Ejercicio 1 — Intercambiar dos valores (básico, 15 min)
Enunciado. Crea una función void intercambiar(int& a, int& b) que intercambie dos valores. Llámala desde main y muestra el antes y el después.
Rúbrica (10 pts): función (5 pts) + main (3 pts) + verificación (2 pts).
Ejercicio 2 — Incrementar un contador (básico, 15 min)
Enunciado. Crea una función void incrementar(int& contador, int cantidad) que incremente el contador por la cantidad. Verifica con 5 llamadas que el contador suma correctamente.
Rúbrica (10 pts): función (4 pts) + 5 tests (4 pts) + verificación (2 pts).
Ejercicio 3 — Encontrar mayor y menor (básico, 20 min)
Enunciado. Crea una función void encontrarExtremos(const int arr[], int n, int& mayor, int& menor) que encuentre el mayor y el menor de un arreglo. El arreglo se pasa por referencia constante (no se copia), los resultados por referencia.
Rúbrica (10 pts): función (5 pts) + main con arreglo de prueba (3 pts) + output correcto (2 pts).
Ejercicio 4 — Proyecto multi-archivo "Calculadora" (intermedio, 30 min)
Enunciado. Crea un proyecto con 3 archivos:
- math_utils.h: prototipos de sumar, restar, multiplicar, dividir.
- math_utils.cpp: implementaciones.
- main.cpp: menú con las 4 operaciones.
Compila con g++ main.cpp math_utils.cpp -o calculadora.exe.
Rúbrica (10 pts): 3 archivos correctos (5 pts) + menú funcional (3 pts) + compilación (2 pts).
Ejercicio 5 — Proyecto multi-archivo "Estudiante" (avanzado, 30 min)
Enunciado. Crea un proyecto con 4 archivos:
- estudiante.h: struct Estudiante (nombre, edad, promedio) + prototipos.
- estudiante.cpp: funciones para crear, imprimir, calcular promedio.
- main.cpp: crea 3 estudiantes, calcula e imprime sus promedios.
- Compila y ejecuta.
Rúbrica (10 pts): 3 archivos correctos (5 pts) + struct bien definido (2 pts) + compilación (2 pts) + output (1 pt).
Ejercicio 6 — Validación por referencia (avanzado, 30 min)
Enunciado. Crea una función bool validarRango(int& valor, int min, int max) que pida al usuario un valor hasta que esté en el rango. Retorna true si el valor final es válido.
Rúbrica (10 pts): función (5 pts) + uso en main (3 pts) + tests (2 pts).
Ejercicio bonus — Proyecto con 5 archivos (opcional, 30 min)
Enunciado. Crea un proyecto con 5 archivos: main.cpp, menu.h/cpp, operaciones.h/cpp. El menú llama a operaciones que están en archivos separados. Compila todo en un solo ejecutable.
Rúbrica bonus (5 pts): 5 archivos (2 pts) + compilación exitosa (2 pts) + funcionalidad (1 pt).
🎯 Cierre del tema
Lo que aprendiste hoy:
- Los 3 tipos de scope: local, global, de bloque.
- Las 3 formas de paso: por valor, por referencia, por puntero.
- Cuándo usar cada tipo de paso (regla de decisión).
- Archivos de cabecera .h con guardas de inclusión.
- Estructura de proyectos multi-archivo profesionales.
- Comando para compilar multi-archivo con g++.
- Comparación con Java y Python.
Lo que sigue: el Boss Final 2 — Sistema de Gestión de Biblioteca (8h), el proyecto integrador del capítulo. Vas a aplicar TODO lo aprendido: condicionales, bucles, patrones, funciones, modularidad, testing. Es el examen final del capítulo y tu segundo proyecto para tu portafolio.
XP obtenida al completar este tema: 100 base + 30 por LAB (6 ejercicios) + 20 por Quest = 150 XP.
*"Un programa sin modularidad es un cuarto desordenado. Un programa con módulos es una casa bien organizada: cada cosa en su lugar, y cuando necesitas algo, sabes dónde encontrarlo."* — De la introducción de este manual.
Fin del Tema 2.10
📝 Quest · Cuestionario — 📝 Quest — Reactivos para autoevaluación
Las siguientes preguntas te sirven para autoevaluarte después de leer el tema. Responde en tu cuaderno o mentalmente, y luego revisa las Respuestas modelo (disponibles en libre acceso, sin iniciar sesión).
Sección A · Opción múltiple
Elige la opción correcta (A, B, C o D). Las respuestas están en la sección Respuestas modelo.
- ¿Qué es el scope de una variable?
- Su tipo
- La región del código donde la variable es visible
- Su valor
- Su nombre
- Una variable local existe:
- En todo el programa
- Solo dentro de la función donde se declara
- En otros archivos
- Siempre
- Una variable global existe:
- Solo en una función
- En todo el archivo (o más si es extern)
- Solo en main
- Solo en clases
- Paso por valor significa:
- Se pasa la variable original
- Se pasa una copia; cambios no afectan al original
- Se pasa un puntero
- Se pasa la referencia
- Paso por referencia significa:
- Copia
- Se pasa el original; cambios SÍ afectan al original
- Constante
- Puntero null
- En C++, ¿cómo se declara paso por referencia?
- tipo funcion(tipo x)
- tipo funcion(tipo& x)
- tipo funcion(tipo* x)
- tipo funcion(const tipo x)
- ¿Qué es multi-archivo en C++?
- Un solo archivo
- Dividir el código en varios .cpp/.h para mejor organización
- Compilar varias veces
- Comprimir archivos
- ¿Para qué sirve un header (.h)?
- Compilar
- Declarar funciones/clases que otros archivos usarán
- Ejecutar
- Es un comentario
- ¿Qué hace #ifndef X / #define X / #endif?
- Compila
- Evita que un header se incluya múltiples veces
- Ejecuta
- Renombra
- En C++, ¿qué archivo implementa una función declarada en funciones.h?
- funciones.h
- funciones.cpp
- main.cpp solamente
- Cualquiera
Sección B · Preguntas abiertas
Desarrolla tu respuesta en al menos 3 líneas. Compara con la respuesta modelo después de escribir.
- Explica la diferencia entre variable local, global y estática. Da un ejemplo de cada una.→ Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
- ¿Por qué las variables globales son generalmente una mala práctica?→ Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
- Diseña un sistema multi-archivo en C++ para una calculadora: calculadora.h (interfaz), calculadora.cpp (implementación), main.cpp (uso). Explica qué va en cada archivo.→ Escribe tu respuesta aquí (en tu cuaderno o mentalmente)…
🟢 Cuando termines, revisa las Respuestas modelo y compáralas con las tuyas. La mejor forma de aprender es discutir cada respuesta contigo mismo o con un compañero.