Ir al contenido principal
Bethemesh
HistoriaHistoria de la informática

C++: de C with Classes a un lenguaje de propósito general

De C with Classes al C++ moderno, descubre cómo el lenguaje concilió abstracción, programación genérica, compatibilidad y control del rendimiento.

Publicado el 10 de agosto de 2026Lectura : 24 minPor Equipo Bethemesh
Principiante
C++: de C with Classes a un lenguaje de propósito general
Mostrar el contenido
  1. Antes de C++: el éxito de C
  2. Bjarne Stroustrup y la influencia de Simula
  3. 1979: «C with Classes»
  4. Cfront: transformar C++ en C
  5. 1983: C with Classes se convierte en C++
  6. 1985: la primera difusión comercial
  7. La programación orientada a objetos se vuelve central
  8. «Zero-overhead abstractions»
  9. Gestión determinista de los recursos
  10. Los templates transforman profundamente C++
  11. El nacimiento de la STL
  12. ¿Por qué es tan importante la STL?
  13. La normalización se vuelve indispensable
  14. C++98: el lenguaje se convierte en un estándar internacional
  15. Los años 2000: potencia y reputación de complejidad
  16. Java propone otra respuesta
  17. C++11: el renacimiento del lenguaje
  18. La semántica de movimiento
  19. Los punteros inteligentes cambian las prácticas
  20. C++ moderno: evitar el C++ «histórico»
  21. C++14: consolidar C++11
  22. C++17: enriquecer el lenguaje y su biblioteca
  23. C++20: concepts, ranges y coroutines
  24. C++23 y la evolución continua
  25. ¿Por qué no eliminar simplemente las funcionalidades antiguas?
  26. ¿C++ es realmente compatible con C?
  27. Un lenguaje multiparadigma
  28. Metaprogramación: cuando calcula el compilador
  29. ¿Por qué se sigue utilizando C++?
  30. Rendimiento
  31. Predictibilidad
  32. Ecosistema
  33. Interoperabilidad
  34. Compiladores
  35. Portabilidad
  36. Videojuegos y motores 3D
  37. Navegadores y software complejo
  38. Sistemas embebidos y restricciones de hardware
  39. Finanzas, cálculo científico e infraestructuras
  40. El coste de esta potencia: la complejidad
  41. La seguridad de memoria se convierte en un reto fundamental
  42. C++ frente a lenguajes más recientes
  43. ¿Por qué C++ no se ha rediseñado completamente?
  44. El papel del comité ISO
  45. C++ ya no es el lenguaje de 1985
  46. C++ y la idea de «lenguaje de propósito general»
  47. Un lenguaje moldeado por sus restricciones
  48. De C with Classes al C++ moderno
  49. Lo esencial
  50. Preguntas frecuentes
  51. ¿Quién creó C++?
  52. ¿Cómo se llamaba originalmente C++?
  53. ¿Por qué el lenguaje se llama C++?
  54. ¿C++ es simplemente una versión orientada a objetos de C?
  55. ¿Cuál es la diferencia entre C y C++?
  56. ¿Por qué es tan importante C++11?
  57. ¿C++ utiliza un recolector de basura?
  58. ¿Se sigue utilizando C++ hoy?
  59. ¿Por qué se considera difícil C++?
  60. ¿C++ será sustituido?

C++ ocupa un lugar particular en la historia de los lenguajes de programación. Nacido a finales de la década de 1970 en Bell Labs, no buscaba sustituir radicalmente las herramientas existentes. Su ambición era más delicada: añadir mecanismos de abstracción potentes sin renunciar al control, la eficiencia y la proximidad a la máquina que habían hecho triunfar al lenguaje C.

Esta tensión acompaña a C++ desde sus orígenes.

El lenguaje debe permitir construir grandes programas estructurados y, al mismo tiempo, seguir siendo adecuado para sistemas en los que importan el rendimiento, la memoria y los recursos de hardware. Debe evolucionar sin inutilizar una inmensa cantidad de código existente. Debe admitir varios estilos de programación sin imponer un único modelo.

Más de cuarenta años después de sus comienzos, C++ sigue utilizándose en motores de videojuegos, sistemas operativos, navegadores, software científico, infraestructuras financieras, aplicaciones embebidas y numerosos componentes en los que el rendimiento es esencial.

Su historia es, por tanto, la de un compromiso permanente entre abstracción y control.

Antes de C++: el éxito de C

Para comprender C++, primero hay que hablar de C.

A comienzos de la década de 1970, Dennis Ritchie desarrolló C en Bell Labs en el contexto de la creación de Unix. El lenguaje ofrecía un equilibrio extraordinario: permitía escribir código relativamente portable y, al mismo tiempo, proporcionaba al programador un control preciso sobre la memoria y los recursos de la máquina.

C se convirtió rápidamente en uno de los grandes lenguajes de programación de sistemas.

Nuestro artículo sobre la historia del lenguaje C explica con más detalle esta evolución.

Sin embargo, a medida que los programas crecían, aparecía una dificultad: ¿cómo organizar sistemas complejos sin perder el rendimiento y el control que ofrecía C?

C permitía estructurar el código mediante funciones, estructuras y módulos, pero no proporcionaba de forma nativa los mecanismos de abstracción que algunos lenguajes de investigación ya estaban explorando.

Ese era precisamente el problema que interesaba a un joven investigador danés: Bjarne Stroustrup.

Bjarne Stroustrup y la influencia de Simula

A finales de los años setenta, Bjarne Stroustrup trabajaba en su tesis doctoral en la Universidad de Cambridge.

Entre otros lenguajes, utilizaba Simula, desarrollado en Noruega durante la década de 1960 y considerado con frecuencia uno de los principales antecesores de la programación orientada a objetos.

Simula proponía conceptos muy potentes: clases, objetos, herencia y métodos virtuales.

Para Stroustrup, estas abstracciones facilitaban considerablemente el modelado de sistemas complejos.

Pero también encontró una limitación: el rendimiento de las herramientas que utilizaba no respondía a sus necesidades.

Cuando se incorporó a Bell Labs en 1979, trabajó en problemas relacionados con sistemas distribuidos. Quería disponer de la capacidad de abstracción de Simula sin perder la eficiencia de C.

La idea fundadora de C++ podría resumirse casi así:

¿y si fuera posible disponer de las clases de Simula sin renunciar al rendimiento y al ecosistema de C?

La biografía de Bjarne Stroustrup permite profundizar en su trayectoria y en las decisiones que guiaron el diseño del lenguaje.

1979: «C with Classes»

El primer proyecto no se llamaba C++.

A partir de 1979, Stroustrup desarrolló un lenguaje denominado C with Classes, literalmente «C con clases».

El nombre describía muy bien la intención inicial.

No se trataba de empezar desde cero, sino de ampliar C con mecanismos que permitieran estructurar mejor los programas.

C with Classes introdujo, entre otras cosas:

  • clases;
  • constructores y destructores;
  • control de acceso;
  • determinadas formas de herencia;
  • argumentos predeterminados;
  • funciones integradas en el modelo de clases.

El objetivo ya era ayudar al programador a expresar abstracciones sin imponer un coste de ejecución innecesario.

Esta idea se convertiría en uno de los principios históricos de C++: no pagar por aquello que no se utiliza.

Cfront: transformar C++ en C

Los primeros desarrollos de C++ se beneficiaron de una estrategia pragmática.

En lugar de construir inmediatamente toda una infraestructura de compilación independiente, Stroustrup desarrolló Cfront.

Cfront traducía el código C with Classes, y posteriormente C++, a código C.

Después, el resultado podía compilarse mediante los compiladores de C ya disponibles en diferentes plataformas.

Este enfoque presentaba varias ventajas.

Facilitaba la experimentación.

Permitía al nuevo lenguaje aprovechar el ecosistema existente de C.

También demostraba que un lenguaje con abstracciones más ricas podía transformarse en código suficientemente próximo a los mecanismos de bajo nivel.

Cfront desempeñó un papel importante durante los primeros años de C++, antes de que los compiladores nativos fueran tomando el relevo.

1983: C with Classes se convierte en C++

A comienzos de la década de 1980, el lenguaje evolucionó rápidamente.

En 1983 recibió su nombre definitivo: C++.

El nombre fue propuesto por Rick Mascitti.

En C, el operador ++ incrementa un valor.

«C++» sugiere así, con cierto humor, un C mejorado o incrementado.

El nombre refleja perfectamente la filosofía del proyecto: C++ mantiene una fuerte continuidad con C y, al mismo tiempo, añade nuevas capacidades.

Por entonces aparecieron o se reforzaron varios elementos importantes:

  • funciones virtuales;
  • sobrecarga de funciones;
  • sobrecarga de operadores;
  • referencias;
  • constantes;
  • una comprobación de tipos más estricta.

El lenguaje empezaba a adquirir la forma por la que acabaría siendo reconocible.

1985: la primera difusión comercial

En 1985 apareció la primera edición de The C++ Programming Language, escrita por Bjarne Stroustrup.

Ese mismo año estuvo disponible la primera versión comercial de C++.

Este periodo marcó la salida del lenguaje del entorno puramente experimental de Bell Labs.

C++ comenzó a adoptarse en proyectos industriales reales.

Su principal argumento resultaba atractivo: permitir a los desarrolladores acostumbrados a C construir abstracciones más estructuradas sin abandonar el control de los recursos.

Esta promesa interesaba especialmente en ámbitos donde el coste de las abstracciones debía seguir siendo previsible.

La programación orientada a objetos se vuelve central

Durante las décadas de 1980 y 1990, la programación orientada a objetos experimentó un fuerte crecimiento.

La idea general consistía en organizar los programas alrededor de objetos que reunían datos y comportamientos.

C++ contribuyó ampliamente a la difusión de este enfoque.

Las clases, la encapsulación, la herencia y el polimorfismo permitían representar jerarquías y definir interfaces comunes.

Las funciones virtuales desempeñaban, en particular, un papel esencial.

Permitían seleccionar en tiempo de ejecución la función correspondiente al tipo real de un objeto.

Esta capacidad hace posible el polimorfismo dinámico.

Pero C++ conservó una filosofía particular: estos mecanismos no eran obligatorios.

Un programa podía utilizar clases sin herencia, emplear funciones libres, manipular directamente la memoria o adoptar otros estilos.

Por ello, C++ nunca se convirtió en un lenguaje exclusivamente orientado a objetos.

Para situar estos conceptos en su contexto histórico, nuestro artículo sobre la programación orientada a objetos recorre el camino desde Simula y Smalltalk hasta los lenguajes modernos.

«Zero-overhead abstractions»

Una idea fue convirtiéndose progresivamente en emblema de la filosofía de C++: las abstracciones sin sobrecoste impuesto.

El principio puede resumirse así:

  • lo que no utilizas no debería costarte;
  • lo que utilizas no debería ser significativamente menos eficiente que una implementación manual equivalente.

Esta ambición explica muchas de las decisiones del lenguaje.

C++ pretende permitir abstracciones de alto nivel y, al mismo tiempo, proporcionar al compilador suficiente información para generar código muy eficiente.

Esto lo diferencia de lenguajes que priorizan un modelo de ejecución más simple o delegan una mayor parte de la gestión de recursos a una máquina virtual o a un recolector de basura.

Esta filosofía es extremadamente potente.

También contribuye a la complejidad del lenguaje.

Gestión determinista de los recursos

C++ desarrolló un modelo especialmente importante en torno a los constructores y destructores.

Cuando se crea un objeto, su constructor puede adquirir un recurso.

Cuando el objeto abandona su ámbito, su destructor puede liberarlo automáticamente.

Esta idea dio lugar a una práctica conocida como RAII (Resource Acquisition Is Initialization).

El recurso no tiene por qué ser memoria.

Puede tratarse de:

  • un archivo;
  • un bloqueo;
  • una conexión;
  • un descriptor del sistema;
  • un recurso gráfico;
  • un bloque de memoria.

La ventaja es considerable: la duración de vida de un recurso puede vincularse a la de un objeto.

Esta gestión determinista sigue siendo hoy una de las características más importantes del C++ moderno.

Los templates transforman profundamente C++

A finales de los años ochenta y comienzos de los noventa, C++ tomó una dirección que iba mucho más allá de la orientación a objetos.

Los templates permitieron escribir código genérico.

En lugar de crear una función diferente para cada tipo de datos, el programador podía definir una operación parametrizada por un tipo.

Por ejemplo, una función genérica podía expresar la idea de buscar un elemento sin depender directamente de que dicho elemento fuera un entero, una cadena u otro tipo compatible.

Esta capacidad abrió el camino a la programación genérica.

Acabaría siendo tan importante para la identidad de C++ como las clases y la herencia.

Fue un giro esencial: reducir C++ a «C con programación orientada a objetos» resultaba cada vez menos correcto.

El nacimiento de la STL

Uno de los acontecimientos más importantes de la historia del lenguaje fue la integración de la Standard Template Library, o STL.

La STL procede en gran medida de los trabajos de Alexander Stepanov y sus colaboradores sobre programación genérica.

Su enfoque se basa en una idea potente: separar las estructuras de datos de los algoritmos que las manipulan mediante interfaces genéricas.

Así aparecen conceptos que se hicieron familiares para los desarrolladores de C++:

  • vector;
  • list;
  • map;
  • iteradores;
  • algoritmos como la ordenación o la búsqueda.

En lugar de crear una función de ordenación distinta para cada contenedor, puede definirse un algoritmo genérico que trabaje con iteradores compatibles.

Este diseño influyó profundamente en C++.

La biblioteca estándar se convirtió progresivamente en una parte esencial del lenguaje tal como se utiliza realmente.

¿Por qué es tan importante la STL?

La STL demuestra que es posible combinar:

  • genericidad;
  • reutilización;
  • abstracción;
  • rendimiento.

Una abstracción bien diseñada no implica necesariamente una penalización importante durante la ejecución.

El compilador puede instanciar y optimizar el código genérico para los tipos que realmente se utilizan.

Esta filosofía reforzó de manera duradera la cultura de C++.

El lenguaje ya no era simplemente un C enriquecido con clases.

Se convirtió en un entorno multiparadigma, capaz de combinar programación procedimental, orientada a objetos y genérica.

La normalización se vuelve indispensable

A medida que C++ se difundía, aparecieron múltiples compiladores.

Entonces surgió un problema evidente: sin una norma común, cada implementación corría el riesgo de desarrollar su propio dialecto.

La estandarización se volvió esencial.

Se puso en marcha un proceso internacional de normalización dentro de ISO.

El objetivo era definir con precisión el lenguaje y su biblioteca estándar para que un programa conforme pudiera trasladarse más fácilmente entre distintas implementaciones.

Este proceso desembocó en 1998 en la primera gran norma internacional de C++.

C++98: el lenguaje se convierte en un estándar internacional

C++98 constituye una etapa fundamental.

Por primera vez, el lenguaje disponía de una norma ISO completa.

Esta formalizó numerosas funcionalidades desarrolladas durante los años anteriores e integró la STL en la biblioteca estándar.

El C++ estandarizado contaba ya con una base común para fabricantes de compiladores y desarrolladores.

Una corrección de la norma, conocida habitualmente como C++03, llegó algunos años después.

Sin embargo, durante un largo periodo, C++98/03 siguió siendo la referencia principal.

Esta estabilidad favoreció la adopción industrial.

Pero también tuvo un precio: mientras las prácticas informáticas evolucionaban rápidamente, el propio lenguaje daba en ocasiones la impresión de avanzar con lentitud.

Los años 2000: potencia y reputación de complejidad

A comienzos de la década de 2000, C++ estaba enormemente extendido.

Se utilizaba en software de escritorio, videojuegos, sistemas, aplicaciones financieras, herramientas científicas y numerosas infraestructuras.

Pero su reputación también se volvió más ambivalente.

El lenguaje acumulaba varias décadas de funcionalidades.

Conservaba una fuerte compatibilidad con C y con código C++ antiguo.

La gestión manual de memoria seguía siendo habitual.

Errores como desbordamientos de búfer, punteros inválidos, dobles liberaciones o fugas de memoria podían provocar fallos difíciles de diagnosticar.

Los templates generaban a veces mensajes de error intimidantes.

La sintaxis contenía numerosos casos particulares.

C++ era potente, pero esa potencia exigía una disciplina considerable.

Java propone otra respuesta

En la década de 1990 apareció Java con una filosofía diferente.

El lenguaje adoptó una sintaxis familiar para los desarrolladores de C y C++, pero eliminó o restringió varios mecanismos considerados peligrosos o demasiado complejos.

Se apoyó especialmente en una máquina virtual y en un recolector de basura para automatizar parte de la gestión de memoria.

Esta diferencia ilustra dos enfoques.

C++ favorece intensamente el control, la compatibilidad y la posibilidad de optimizar cerca de los recursos subyacentes.

Java acepta una mayor abstracción durante la ejecución a cambio de un entorno más uniforme y una gestión de memoria más automatizada.

Nuestro artículo sobre la historia de Java permite comparar estas trayectorias.

Los dos lenguajes no responden exactamente a las mismas restricciones, y su coexistencia demuestra que no existe una única forma de diseñar un lenguaje de propósito general.

C++11: el renacimiento del lenguaje

La siguiente gran transformación llegó en 2011.

La norma C++11 fue tan importante que suele considerarse el comienzo del C++ moderno.

Aparecieron o se volvieron realmente prácticas numerosas funcionalidades:

  • auto para la inferencia de tipos;
  • expresiones lambda;
  • referencias rvalue;
  • semántica de movimiento;
  • punteros inteligentes estándar;
  • nullptr;
  • bucles for basados en rangos;
  • mejor soporte para programación concurrente;
  • constexpr;
  • listas de inicialización;
  • templates variádicos.

Esta lista representa solo una parte de los cambios.

C++11 no se limitó a añadir funcionalidades.

Cambió la forma recomendada de escribir C++.

La semántica de movimiento

Entre las innovaciones de C++11, la semántica de movimiento representa especialmente bien la filosofía del lenguaje.

Imaginemos un objeto que contiene una gran cantidad de datos.

Copiarlo puede resultar costoso.

Pero si el objeto de origen ya no va a utilizarse, puede ser posible transferir directamente sus recursos al nuevo objeto en lugar de copiarlo todo.

Las referencias rvalue y las operaciones de movimiento permiten expresar esta situación.

El lenguaje obtiene así una abstracción más expresiva sin sacrificar innecesariamente el rendimiento.

Es un excelente ejemplo del objetivo histórico de C++: permitir abstracciones de alto nivel manteniendo un control preciso sobre su coste.

Los punteros inteligentes cambian las prácticas

Durante mucho tiempo, aprender C++ significaba a menudo aprender new y delete desde muy pronto.

El C++ moderno fomenta un enfoque diferente.

Tipos como:

  • std::unique_ptr;
  • std::shared_ptr;
  • std::weak_ptr

permiten expresar de manera más explícita la propiedad y la duración de vida de los objetos.

Combinados con RAII y con los contenedores de la biblioteca estándar, reducen considerablemente la necesidad de gestionar directamente la memoria dinámica en el código de aplicación habitual.

Esto no convierte C++ en un lenguaje con recolector de basura.

La diferencia es importante.

La liberación de recursos suele seguir siendo determinista, pero puede automatizarse mediante los propios tipos.

C++ moderno: evitar el C++ «histórico»

A partir de C++11 se extendió una idea: el código C++ moderno puede ser muy diferente del estilo que se enseñaba en la década de 1990.

Las buenas prácticas recomiendan, entre otras cosas:

  • preferir objetos con duración de vida automática;
  • utilizar contenedores estándar en lugar de arrays manuales cuando sea apropiado;
  • utilizar punteros inteligentes para expresar la propiedad;
  • evitar asignaciones explícitas innecesarias;
  • aprovechar algoritmos y abstracciones de la biblioteca estándar;
  • limitar las conversiones peligrosas;
  • sacar mayor partido del sistema de tipos.

Esta evolución es importante.

Parte de la reputación de C++ procede de prácticas antiguas que siguen siendo técnicamente posibles, pero que ya no representan necesariamente la forma recomendada de escribir programas nuevos.

C++14: consolidar C++11

La norma C++14 fue deliberadamente menos revolucionaria.

Mejoró y completó mecanismos introducidos en 2011.

El objetivo era, entre otras cosas, hacer determinadas funcionalidades más sencillas y coherentes.

C++ entró entonces en un ritmo de evolución más regular.

En lugar de esperar más de una década entre dos grandes transformaciones, la comunidad pasó a una cadencia aproximada de tres años entre nuevas normas.

C++17: enriquecer el lenguaje y su biblioteca

C++17 continuó esta modernización.

Entre sus incorporaciones importantes se encuentran:

  • std::optional;
  • std::variant;
  • std::any;
  • std::string_view;
  • structured bindings;
  • if constexpr;
  • importantes mejoras de la biblioteca estándar.

Estas funcionalidades permiten expresar directamente situaciones que antes requerían convenciones o bibliotecas externas.

std::optional, por ejemplo, permite representar explícitamente un valor que puede estar ausente.

El propio tipo documenta esa posibilidad.

Este enfoque ilustra una tendencia del C++ moderno: utilizar el sistema de tipos para hacer más explícitas las intenciones del programa.

C++20: concepts, ranges y coroutines

C++20 representa otra etapa importante.

Tres familias de funcionalidades atrajeron especialmente la atención:

  • los concepts;
  • los ranges;
  • las coroutines.

Los concepts permiten expresar con mayor claridad las restricciones impuestas a los parámetros de templates.

Se trata de una evolución importante para la programación genérica.

Durante mucho tiempo, un error al utilizar un template podía producir diagnósticos extremadamente complejos.

Los concepts permiten indicar de manera más explícita qué debe ser capaz de hacer un tipo para ser aceptado.

Los ranges ofrecen una forma más expresiva de componer operaciones sobre secuencias.

Las coroutines proporcionan mecanismos del lenguaje para suspender y reanudar la ejecución de una función, útiles especialmente para determinados modelos asíncronos.

C++20 también introdujo los módulos en el estándar, con la ambición de ofrecer una alternativa moderna a determinados usos históricos de los archivos de cabecera.

C++23 y la evolución continua

C++23 prosigue este ciclo de mejora.

Como ocurre a menudo con C++, evolucionar no significa sustituir bruscamente el lenguaje existente.

Se añaden nuevas funcionalidades mientras la inmensa base de código histórica sigue siendo importante.

La biblioteca estándar continúa creciendo.

Los mecanismos de programación genérica siguen evolucionando.

El uso de constexpr permite realizar cada vez más cálculos durante la compilación.

Las herramientas, los compiladores y los analizadores estáticos también progresan.

Esta evolución incremental es a la vez una fortaleza y una dificultad.

C++ puede seguir modernizando las prácticas.

Pero debe cargar con una historia extraordinariamente larga.

¿Por qué no eliminar simplemente las funcionalidades antiguas?

La pregunta parece natural.

Si algunas construcciones están hoy desaconsejadas, ¿por qué no retirarlas?

Porque una de las grandes fortalezas de C++ es su inmenso ecosistema existente.

Las empresas utilizan millones de líneas de código desarrolladas durante décadas.

Bibliotecas y sistemas críticos deben poder seguir manteniéndose.

Eliminar bruscamente una funcionalidad puede romper una cantidad considerable de software.

La compatibilidad es, por tanto, una restricción fundamental en la evolución del lenguaje.

Esta política contribuye a su longevidad.

Pero también explica parte de su complejidad: varias generaciones de prácticas pueden coexistir dentro del mismo lenguaje.

¿C++ es realmente compatible con C?

La relación entre C y C++ es más sutil que un simple «sí».

C++ fue concebido históricamente como una extensión de C y mantiene una gran proximidad sintáctica y conceptual con él.

Muchos programas C pueden adaptarse con facilidad.

Los dos lenguajes también comparten convenciones y pueden interactuar mediante interfaces adecuadas.

Pero C y C++ son hoy dos lenguajes distintos, cada uno con su propia norma y su propia evolución.

Un programa C válido no es necesariamente un programa C++ válido.

A la inversa, los patrones recomendados en el C++ moderno suelen diferir considerablemente de los utilizados en C.

Comprender esta distinción evita reducir C++ a «C con algunas funcionalidades adicionales».

Un lenguaje multiparadigma

A veces se presenta C++ como un lenguaje orientado a objetos.

Esa descripción es incompleta.

El lenguaje permite varios estilos:

  • programación procedimental;
  • programación orientada a objetos;
  • programación genérica;
  • programación funcional en determinadas construcciones;
  • metaprogramación en tiempo de compilación;
  • programación próxima al sistema.

Un mismo programa puede combinar varios de estos enfoques.

Esta flexibilidad es una de las grandes fortalezas de C++.

Permite escoger abstracciones adecuadas para diferentes problemas.

Pero también significa que suele haber varias formas razonables de realizar una misma tarea.

Metaprogramación: cuando calcula el compilador

Los templates tuvieron un efecto inesperado.

Los desarrolladores descubrieron que podían utilizarlos para realizar cálculos durante la compilación.

La template metaprogramming se convirtió así en un ámbito propio.

Al principio, algunas de estas posibilidades casi parecían un uso ingenioso e imprevisto del sistema de templates.

Con las sucesivas versiones, C++ incorporó mecanismos más directos, especialmente constexpr y posteriormente consteval.

El lenguaje permite ahora ejecutar una cantidad creciente de lógica durante la compilación.

Esto puede servir para comprobar propiedades, preparar datos o eliminar determinados costes durante la ejecución.

Una vez más aparece el objetivo de conseguir abstracción sin sacrificar rendimiento.

¿Por qué se sigue utilizando C++?

Cada vez que aparece un nuevo lenguaje de sistemas, en ocasiones se anuncia la desaparición de C++.

Sin embargo, sigue estando enormemente presente.

Varias razones explican esta longevidad.

Rendimiento

C++ permite producir código nativo de muy alto rendimiento.

El programador dispone de un control importante sobre las asignaciones, la disposición de los datos, las copias y los recursos.

Predictibilidad

En muchos sistemas, no basta con ser rápido de media.

También es necesario controlar cuándo se producen determinadas operaciones costosas.

La gestión determinista de recursos de C++ resulta valiosa en este contexto.

Ecosistema

Décadas de desarrollo han producido una cantidad inmensa de bibliotecas, motores, frameworks y bases de código.

Interoperabilidad

C++ puede interactuar estrechamente con C y con numerosas API del sistema.

Compiladores

GCC, Clang y Microsoft Visual C++ son herramientas extremadamente maduras y optimizadas.

Portabilidad

C++ está disponible en una enorme variedad de plataformas, desde microcontroladores hasta servidores de gran potencia.

Videojuegos y motores 3D

Los videojuegos constituyen uno de los ámbitos más visibles en los que C++ sigue siendo dominante.

Los motores deben gestionar:

  • renderizado gráfico;
  • física;
  • audio;
  • inteligencia artificial;
  • red;
  • memoria;
  • carga de recursos;
  • restricciones de tiempo real.

El rendimiento y el control preciso de los recursos son esenciales.

Por ello, muchos grandes motores y videojuegos utilizan C++ de forma intensiva.

Esto no significa que todo el código de un videojuego deba escribirse en C++.

Los lenguajes de scripting y las herramientas de mayor nivel suelen utilizarse como complemento.

Pero C++ permanece con frecuencia en el núcleo de los componentes más exigentes.

Los navegadores modernos ofrecen otro buen ejemplo.

Mostrar una página web implica motores de renderizado, motores JavaScript, red, seguridad, multimedia, gráficos y una enorme cantidad de lógica.

Muchos componentes de los navegadores se han escrito históricamente en C++.

El lenguaje también se utiliza en software de creación gráfica, herramientas de diseño, bases de datos y aplicaciones científicas.

Su terreno natural sigue siendo a menudo aquel en el que se combinan software de gran tamaño y fuertes exigencias de rendimiento.

Sistemas embebidos y restricciones de hardware

C++ también está muy presente en los sistemas embebidos.

En estos entornos, la memoria puede ser limitada.

El consumo energético puede ser importante.

El hardware puede imponer restricciones precisas.

Algunas aplicaciones no pueden depender de una máquina virtual pesada ni de un recolector de basura cuyas pausas sean difíciles de predecir.

C++ permite utilizar abstracciones modernas manteniendo un control próximo al hardware.

Naturalmente, no todas las funcionalidades del lenguaje se utilizan en todos los sistemas embebidos.

Es habitual emplear subconjuntos y reglas estrictas de desarrollo.

Finanzas, cálculo científico e infraestructuras

En las finanzas de baja latencia, unos pocos microsegundos pueden importar.

En el cálculo científico, grandes cantidades de datos deben procesarse con eficiencia.

En determinadas infraestructuras, el consumo de CPU y memoria a gran escala se convierte en una cuestión económica.

Estas restricciones siguen creando un espacio importante para C++.

El lenguaje permite construir abstracciones sofisticadas y, al mismo tiempo, acceder a los detalles que se vuelven importantes cuando el rendimiento alcanza sus límites.

El coste de esta potencia: la complejidad

La longevidad de C++ tiene una contrapartida.

El lenguaje es enorme.

Contiene funcionalidades heredadas de distintas épocas.

Algunas construcciones interactúan de manera sutil.

Las reglas sobre duración de vida, sobrecarga, resolución de templates o conversiones pueden volverse complejas.

Un principiante puede escribir rápidamente un pequeño programa en C++.

Dominar realmente el lenguaje requiere mucho más tiempo.

Esta dificultad no es simplemente un defecto accidental.

Procede en parte de los objetivos contradictorios perseguidos por C++:

  • seguir siendo eficiente;
  • conservar la compatibilidad;
  • ofrecer abstracciones potentes;
  • funcionar en numerosas plataformas;
  • admitir varios estilos de programación;
  • evolucionar sin romper el software existente.

La seguridad de memoria se convierte en un reto fundamental

Una parte importante de las vulnerabilidades del software de sistemas ha procedido históricamente de errores de gestión de memoria.

C++ permite escribir código seguro y estructurado, especialmente gracias a RAII, los contenedores estándar y las prácticas modernas.

Pero también sigue permitiendo operaciones peligrosas de bajo nivel cuando el programador las solicita.

Esta capacidad es útil en determinados ámbitos.

También constituye un riesgo.

Los debates actuales sobre los lenguajes denominados memory-safe han reavivado por ello la reflexión sobre el lugar de C++.

Lenguajes más recientes intentan ofrecer un rendimiento de sistemas comparable y, al mismo tiempo, impedir más errores de memoria desde la compilación.

Esta competencia también impulsa al ecosistema C++ a mejorar sus herramientas, recomendaciones y mecanismos de seguridad.

C++ frente a lenguajes más recientes

C++ ha visto aparecer varias generaciones de competidores.

Java propuso portabilidad y gestión automatizada de memoria.

C# desarrolló un entorno moderno alrededor de .NET.

Go priorizó la simplicidad y la concurrencia.

Rust intenta combinar rendimiento de sistemas con fuertes garantías de seguridad de memoria.

Otros lenguajes siguen explorando nuevos enfoques.

Sin embargo, sustituir C++ no es únicamente una cuestión de calidad sintáctica.

También hay que considerar:

  • las bases de código existentes;
  • las bibliotecas;
  • las herramientas;
  • las competencias disponibles;
  • las plataformas compatibles;
  • las restricciones de migración;
  • la interoperabilidad con sistemas ya en producción.

Por ello, la evolución de los lenguajes suele producirse mucho más por coexistencia que por sustitución instantánea.

¿Por qué C++ no se ha rediseñado completamente?

Un lenguaje nuevo podría ser más sencillo si abandonara toda compatibilidad.

Pero ya no sería C++.

El proyecto C++ se basa históricamente en una evolución continua.

Esta continuidad permite que el código antiguo sobreviva y que sistemas gigantescos migren de manera gradual.

El precio es una complejidad acumulada.

C++ vive así con una contradicción estructural:

para seguir siendo moderno debe evolucionar; para conservar su valor industrial no puede olvidar demasiado deprisa su pasado.

Una gran parte del trabajo del comité de estandarización consiste precisamente en gestionar esta tensión.

El papel del comité ISO

Desde su estandarización, C++ ya no es el proyecto de una sola persona.

Bjarne Stroustrup sigue siendo una figura fundamental de su historia y continúa participando en los debates, pero la evolución del lenguaje es colectiva.

Representantes de empresas, de la investigación, de fabricantes de compiladores y de la comunidad proponen y evalúan cambios.

Las decisiones deben tener en cuenta numerosos factores:

  • utilidad;
  • rendimiento;
  • compatibilidad;
  • posibilidad de implementación;
  • coherencia con el resto del lenguaje;
  • experiencia de los desarrolladores.

Esta gobernanza explica en ocasiones la lentitud de ciertas evoluciones.

Pero también es necesaria para un lenguaje utilizado en sistemas críticos a escala mundial.

C++ ya no es el lenguaje de 1985

Comparar el C++ actual con su primera versión muestra una transformación considerable.

El lenguaje de 1985 estaba fuertemente centrado en añadir mecanismos orientados a objetos a C.

El C++ moderno dispone de:

  • una amplia biblioteca estándar;
  • contenedores genéricos;
  • algoritmos;
  • lambdas;
  • herramientas de gestión automática de recursos;
  • concurrencia estandarizada;
  • mecanismos de cálculo en tiempo de compilación;
  • concepts;
  • ranges;
  • coroutines;
  • módulos;
  • un sistema de templates extremadamente potente.

Sin embargo, sigue siendo reconocible una línea directriz:

proporcionar abstracciones útiles sin quitar al programador la posibilidad de controlar su coste.

C++ y la idea de «lenguaje de propósito general»

C++ no es únicamente un lenguaje de sistemas.

Se ha utilizado para construir prácticamente todos los tipos de software.

Pero no pretende necesariamente ser el lenguaje más sencillo para cada tarea.

Para un pequeño script, Python puede permitir escribir el programa más rápidamente.

Para determinadas aplicaciones web, otros ecosistemas resultan más naturales.

Para algunos nuevos proyectos de sistemas, Rust puede aportar garantías interesantes.

La fortaleza de C++ se encuentra en otro lugar.

Ofrece una combinación poco frecuente de:

  • rendimiento;
  • control;
  • abstracción;
  • portabilidad;
  • madurez;
  • compatibilidad con un inmenso patrimonio de software.

Esta combinación explica su permanencia.

Un lenguaje moldeado por sus restricciones

La historia de C++ se cuenta a veces como una acumulación de funcionalidades.

Esta interpretación deja escapar lo esencial.

Muchas de sus características se explican por las restricciones que el lenguaje intenta respetar simultáneamente.

Quiere ofrecer abstracciones más ricas que C.

Quiere seguir siendo utilizable en programas donde el rendimiento importa.

Quiere funcionar sin imponer un entorno de ejecución pesado.

Quiere conservar una gran compatibilidad con décadas de código.

Quiere evolucionar junto con las prácticas modernas.

Estos objetivos no siempre son compatibles.

La complejidad de C++ es, en parte, el resultado de esta negociación permanente.

De C with Classes al C++ moderno

El camino recorrido desde 1979 es considerable.

Al principio, Stroustrup buscaba sobre todo añadir a C mecanismos inspirados en Simula.

Después llegaron las funciones virtuales, la sobrecarga y la herencia.

Los templates hicieron emerger la programación genérica.

La STL transformó la biblioteca estándar.

C++98 estabilizó el lenguaje a escala internacional.

C++11 renovó profundamente las prácticas.

C++14, C++17, C++20 y C++23 establecieron un ritmo regular de evolución.

El lenguaje cambia, pero nunca rompe por completo con su historia.

Quizá sea esa su característica más notable.

Lo esencial

C++ nació en 1979, cuando Bjarne Stroustrup intentó combinar las abstracciones de Simula con la eficiencia y el ecosistema del lenguaje C.

Inicialmente denominado C with Classes, el proyecto se convirtió en C++ en 1983.

Sin embargo, su historia no se limita a la programación orientada a objetos.

Los templates y la STL convirtieron la programación genérica en uno de los pilares esenciales del lenguaje.

La estandarización C++98 estableció una norma internacional, mientras que C++11 abrió la era del C++ moderno con lambdas, semántica de movimiento, punteros inteligentes y muchas otras mejoras.

Las versiones posteriores continuaron esta modernización con concepts, ranges, coroutines, módulos y una biblioteca estándar cada vez más rica.

C++ conserva hoy un lugar importante porque combina propiedades difíciles de reunir:

abstracción, rendimiento, control de los recursos, portabilidad y compatibilidad con varias décadas de software.

Esta potencia tiene un coste: el lenguaje es complejo y conserva mecanismos históricos que las prácticas modernas intentan evitar con frecuencia.

Pero esa misma continuidad también explica su longevidad.

Más de cuarenta años después de C with Classes, C++ sigue siendo fiel al problema inicial de Stroustrup: ¿cómo construir software de alto nivel sin perder innecesariamente el control de la máquina?

Preguntas frecuentes

¿Quién creó C++?

C++ fue creado por Bjarne Stroustrup en Bell Labs a partir de 1979.

¿Cómo se llamaba originalmente C++?

El proyecto se llamaba inicialmente C with Classes. Su objetivo era añadir a C mecanismos inspirados especialmente en Simula, en particular las clases.

¿Por qué el lenguaje se llama C++?

En C, el operador ++ incrementa un valor. El nombre C++ sugiere así un «C incrementado». Se adoptó en 1983.

¿C++ es simplemente una versión orientada a objetos de C?

No. La orientación a objetos desempeñó un papel importante en sus comienzos, pero C++ es hoy un lenguaje multiparadigma que también admite programación procedimental, genérica, metaprogramación y otros estilos.

¿Cuál es la diferencia entre C y C++?

C y C++ son hoy dos lenguajes distintos. Comparten una historia, gran parte de su sintaxis y numerosos conceptos, pero cada uno posee su propia norma y sus propias prácticas.

¿Por qué es tan importante C++11?

C++11 modernizó profundamente el lenguaje con las lambdas, auto, la semántica de movimiento, los punteros inteligentes, nullptr, constexpr, la concurrencia estandarizada y numerosas mejoras del sistema de tipos.

¿C++ utiliza un recolector de basura?

C++ no depende normalmente de un garbage collector obligatorio. Favorece especialmente la gestión determinista de recursos mediante RAII, los contenedores y los punteros inteligentes.

¿Se sigue utilizando C++ hoy?

Sí. Sigue muy presente en videojuegos, motores 3D, navegadores, sistemas embebidos, software científico, infraestructuras financieras y numerosas aplicaciones que requieren un alto rendimiento.

¿Por qué se considera difícil C++?

Su larga historia, sus requisitos de compatibilidad, sus múltiples paradigmas y el nivel de control que ofrece han dado lugar a un lenguaje muy amplio. El C++ moderno permite evitar muchas prácticas antiguas, pero dominarlo en profundidad sigue siendo exigente.

¿C++ será sustituido?

Los lenguajes nuevos responden a algunas limitaciones de C++, especialmente en seguridad de memoria o simplicidad. Sin embargo, su enorme ecosistema, su rendimiento, su portabilidad y las bases de código existentes hacen poco probable una sustitución completa a corto plazo. La evolución se producirá más probablemente mediante coexistencia y migraciones progresivas.

Fuentes y referencias

  1. 1.Bjarne Stroustrup --- A History of C++
  2. 2.Bjarne Stroustrup --- The Design and Evolution of C++
  3. 3.ISO C++ Foundation --- El estándar

Colección

Lenguajes de programación

  1. 01Grace Hopper: de los primeros compiladores a COBOL
  2. 02John Backus: FORTRAN, la notación BNF y el rechazo del código máquina
  3. 03Dennis Ritchie: el lenguaje C en el corazón de Unix
  4. 04FORTRAN: demostrar que un compilador podía competir con el ensamblador
  5. 05El lenguaje C: hacer portables los sistemas sin ocultar la máquina
  6. 06Niklaus Wirth: de Pascal a Oberon, diseñar mediante la simplicidad
  7. 07Bjarne Stroustrup: diseñar C++ sin renunciar al rendimiento
  8. 08Pascal: aprender a programar haciendo visible la estructura
  9. 09C++: de C with Classes a un lenguaje de propósito general
  10. 10Programación orientada a objetos: objetos, mensajes y abstracciones reutilizables
  11. 11Guido van Rossum: crear Python para que el código sea legible
  12. 12Brendan Eich: JavaScript, del prototipo de Netscape al estándar web
  13. 13James Gosling: el ingeniero que dio origen a Java
  14. 14Python: legibilidad, baterías incluidas y un ecosistema mundial
  15. 15Java: escribir una vez, ejecutar en cualquier lugar
  16. 16JavaScript: el lenguaje que hizo interactiva la Web
  17. 17Ken Thompson: de Unix a Go, la simplicidad como método
  18. 18John McCarthy: Lisp y la idea de programar con símbolos
  19. 19Alan Kay: Smalltalk y el ordenador como medio personal
  20. 20Barbara Liskov: la abstracción que hizo modular el software
  21. 21Robin Milner: ML, la prueba asistida y los lenguajes de la interacción
  22. 22Brian Kernighan: AWK, Unix y el arte de explicar el código
  23. 23Anders Hejlsberg: de Turbo Pascal a C# y TypeScript
  24. 24Larry Wall: Perl, el lenguaje que conectó las herramientas de Internet
  25. 25Yukihiro Matsumoto: Ruby y la felicidad del programador
  26. 26Rasmus Lerdorf: PHP y la democratización de la Web dinámica
BiografíaHistoria de la informáticaPrincipiante

James Gosling: el ingeniero que dio origen a Java

Cómo James Gosling y el equipo Green de Sun diseñaron Java: de Oak y las máquinas virtuales a la portabilidad y el legado del lenguaje.

17 de agosto de 20266 minLeer

¿Te ha resultado útil este artículo?