Programación · n.º 10 entre los lenguajes más usados

Aprende C++ desde cero

Máximo rendimiento: videojuegos, navegadores, sistemas y programas exigentes.

  • 10 lecciones
  • 10 ejercicios interactivos
  • 17 conceptos clave
  • 24,3 % lo usa*

Qué es C++ y para qué sirve

C++ se compila a código máquina y da un control muy fino sobre la memoria, por eso se usa donde el rendimiento importa: motores de videojuegos, navegadores, programas de edición, bases de datos o sistemas embebidos. Es más exigente que otros lenguajes, pero sus herramientas modernas (C++11 en adelante) lo hacen mucho más seguro.

  • Videojuegos y motores gráficos
  • Programas de alto rendimiento
  • Sistemas y dispositivos

Para empezar: Instala un compilador (GCC, Clang o Visual Studio con C++) y compila cada ejemplo con g++ -std=c++20 main.cpp -o main.

Nivel 1 Básico

Lección 1 · Básico

Tu primer programa

Un programa en C++ empieza en la función main. #include <iostream> trae la parte de la biblioteca estándar que permite escribir en la consola con std::cout y el operador <<.

El compilador traduce el archivo .cpp a un ejecutable. Si cambias el código, hay que volver a compilar.

Ejemplo práctico
// main.cpp
#include <iostream>

int main() {
    std::cout << "¡Hola, mundo!\n";
    std::cout << 2 + 3 << '\n';   // 5
    return 0;
}

// g++ -std=c++20 main.cpp -o main
// ./main

Ejercicio

Completa para escribir en la consola.

std:: << "Hola\n";
Ver la solución

Respuesta: cout. std::cout es la salida estándar; con << le envías lo que quieres mostrar.

Lección 2 · Básico

Variables y tipos

Cada variable tiene un tipo fijo: int, double, bool, char o std::string para texto (con #include <string>). Con auto, el compilador deduce el tipo del valor.

Inicializa siempre las variables: en C++ una variable local sin valor inicial contiene basura.

Ejemplo práctico
#include <iostream>
#include <string>

int main() {
    int unidades = 3;
    double precio = 19.99;
    std::string producto = "Auriculares";
    auto total = unidades * precio;   // double

    std::cout << producto << ": " << total << '\n';
}

Ejercicio

¿Qué hace auto en auto total = unidades * precio;?

Ver la solución

Respuesta: El compilador deduce el tipo del valor. auto deduce el tipo en tiempo de compilación; la variable sigue teniendo un tipo fijo (aquí, double).

Lección 3 · Básico

Condiciones y bucles

if y else deciden qué se ejecuta; for repite con un contador y while mientras se cumpla una condición. Los bloques van entre llaves.

Las comparaciones usan ==, !=, <, >, <= y >=, y se combinan con && (y) y || (o).

Ejemplo práctico
#include <iostream>

int main() {
    for (int i = 1; i <= 5; ++i) {
        if (i % 2 == 0) {
            std::cout << i << " es par\n";
        } else {
            std::cout << i << " es impar\n";
        }
    }
}

Ejercicio

¿Qué calcula i % 2?

Ver la solución

Respuesta: El resto de dividir i entre 2. % es el resto de la división: vale 0 si el número es par.

Lección 4 · Básico

std::vector: listas que crecen

std::vector es la lista dinámica de la biblioteca estándar: crece al añadir elementos con push_back y size() dice cuántos tiene.

El bucle for basado en rango recorre todos los elementos sin manejar posiciones.

Ejemplo práctico
#include <iostream>
#include <vector>

int main() {
    std::vector<int> notas = {7, 8, 9};
    notas.push_back(10);

    int suma = 0;
    for (int n : notas) suma += n;

    std::cout << "Media: " << static_cast<double>(suma) / notas.size() << '\n'; // 8.5
}

Ejercicio

Completa para añadir un 6 al final del vector.

notas.(6);
Ver la solución

Respuesta: push_back. push_back añade un elemento al final del vector.

Nivel 2 Intermedio

Lección 5 · Intermedio

Funciones y referencias

Una función declara el tipo que devuelve, su nombre y sus parámetros. Por defecto los argumentos se copian; con una referencia (&) la función trabaja con la variable original.

Para objetos grandes que no vas a modificar, pásalos como const&: no se copian y tampoco se pueden cambiar.

Ejemplo práctico
#include <iostream>
#include <string>

void duplicar(int& n) { n *= 2; }                 // modifica el original

std::string saludo(const std::string& nombre) {   // sin copia ni cambios
    return "Hola, " + nombre;
}

int main() {
    int x = 5;
    duplicar(x);
    std::cout << x << '\n';               // 10
    std::cout << saludo("Ana") << '\n';
}

Ejercicio

¿Qué ocurre con void duplicar(int& n) al llamar duplicar(x)?

Ver la solución

Respuesta: x cambia, porque se pasa por referencia. Con & el parámetro es otro nombre para la misma variable, así que el cambio se ve fuera.

Lección 6 · Intermedio

Clases

Una clase agrupa datos y funciones. Lo que está en private solo se puede usar desde dentro; lo que está en public, desde cualquier parte.

El constructor prepara el objeto al crearlo; con la lista de inicialización (: saldo_(inicial)) das valor a los atributos.

Ejemplo práctico
#include <iostream>

class Cuenta {
public:
    explicit Cuenta(double inicial) : saldo_(inicial) {}
    void ingresar(double cantidad) { saldo_ += cantidad; }
    double saldo() const { return saldo_; }
private:
    double saldo_;
};

int main() {
    Cuenta c(100);
    c.ingresar(50);
    std::cout << c.saldo() << '\n';   // 150
}

Ejercicio

¿Desde dónde se puede usar un miembro private?

Ver la solución

Respuesta: Solo desde dentro de la propia clase. private oculta el miembro: solo los métodos de la clase pueden usarlo.

Lección 7 · Intermedio

La biblioteca estándar: algoritmos y map

La biblioteca estándar trae contenedores como std::map (pares clave–valor ordenados por clave) y algoritmos listos para usar en <algorithm>, como std::sort o std::count_if.

Antes de escribir tu propio bucle para ordenar o buscar, mira si ya existe un algoritmo: será correcto y eficiente.

Ejemplo práctico
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>

int main() {
    std::vector<int> v = {5, 2, 9, 1};
    std::sort(v.begin(), v.end());                 // 1 2 5 9

    std::map<std::string, int> stock = {{"teclado", 10}, {"raton", 0}};
    stock["monitor"] = 3;
    for (const auto& [producto, unidades] : stock)
        std::cout << producto << ": " << unidades << '\n';
}

Ejercicio

Completa para ordenar el vector v.

std::(v.begin(), v.end());
Ver la solución

Respuesta: sort. std::sort ordena el rango entre los dos iteradores que le pases.

Nivel 3 Avanzado

Lección 8 · Avanzado

Memoria y punteros inteligentes

Un puntero guarda la dirección de memoria de otro dato. Reservar memoria con new obliga a liberarla con delete, y olvidarlo provoca fugas o fallos graves.

En C++ moderno se usan punteros inteligentes: std::unique_ptr libera la memoria solo cuando deja de usarse (RAII). Así no hace falta escribir delete.

Ejemplo práctico
#include <iostream>
#include <memory>

struct Imagen {
    Imagen()  { std::cout << "Cargada\n"; }
    ~Imagen() { std::cout << "Liberada\n"; }
};

int main() {
    {
        auto img = std::make_unique<Imagen>();   // Cargada
        // … usar img …
    }                                            // Liberada (automático)
    std::cout << "Fin\n";
}

Ejercicio

¿Qué ventaja tiene std::unique_ptr frente a new y delete?

Ver la solución

Respuesta: Libera la memoria automáticamente. Cuando el unique_ptr sale de su ámbito, su destructor libera la memoria: no puedes olvidarte de hacerlo.

Lección 9 · Avanzado

Plantillas

Una plantilla (template) es código genérico: escribes la función una vez y el compilador genera una versión para cada tipo con el que la uses.

Toda la biblioteca estándar está hecha con plantillas: std::vector<int> y std::vector<std::string> salen del mismo código.

Ejemplo práctico
#include <iostream>
#include <string>

template <typename T>
T mayor(T a, T b) {
    return a > b ? a : b;
}

int main() {
    std::cout << mayor(3, 7) << '\n';                                   // 7
    std::cout << mayor(std::string("pera"), std::string("kiwi")) << '\n'; // pera
}

Ejercicio

¿Qué hace el compilador con template <typename T>?

Ver la solución

Respuesta: Crea una versión de la función para cada tipo usado. Cada vez que usas la plantilla con un tipo nuevo, el compilador genera el código para ese tipo.

Lección 10 · Avanzado

Herencia y polimorfismo

Una clase puede heredar de otra y redefinir sus métodos virtual con override. Así, una lista de punteros a la clase base puede contener objetos distintos y cada uno responde a su manera.

Una clase base con métodos virtuales debe tener un destructor virtual para que los objetos se destruyan bien.

Ejemplo práctico
#include <iostream>
#include <memory>
#include <vector>

struct Figura {
    virtual ~Figura() = default;
    virtual double area() const = 0;   // método abstracto
};
struct Cuadrado : Figura {
    double lado;
    explicit Cuadrado(double l) : lado(l) {}
    double area() const override { return lado * lado; }
};

int main() {
    std::vector<std::unique_ptr<Figura>> figuras;
    figuras.push_back(std::make_unique<Cuadrado>(3));
    for (const auto& f : figuras) std::cout << f->area() << '\n'; // 9
}

Ejercicio

Completa la palabra que indica que el método redefine uno de la clase base.

double area() const  { return lado * lado; }
Ver la solución

Respuesta: override. override pide al compilador que compruebe que de verdad estás redefiniendo un método virtual.

¿Has terminado? Sigue practicando

Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.

Fuentes consultadas

* Porcentaje de personas que dijeron haber usado C++ en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.