C++

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Lenguaje de programación para fines generales

C++ (pronunciado "C más más") es un lenguaje de programación de propósito general de alto nivel creado por el científico informático danés Bjarne Stroustrup como una extensión del lenguaje de programación C, o "C con Clases". El lenguaje se ha expandido significativamente con el tiempo, y el C++ moderno ahora tiene características genéricas y funcionales orientadas a objetos, además de facilidades para la manipulación de memoria de bajo nivel. Casi siempre se implementa como un lenguaje compilado y muchos proveedores proporcionan compiladores de C++, incluidos Free Software Foundation, LLVM, Microsoft, Intel, Embarcadero, Oracle e IBM, por lo que está disponible en muchas plataformas.

C++ se diseñó teniendo en cuenta la programación de sistemas y el software integrado con recursos limitados y grandes sistemas, con el rendimiento, la eficiencia y la flexibilidad de uso como aspectos destacados de su diseño. C++ también se ha encontrado útil en muchos otros contextos, siendo las fortalezas clave la infraestructura de software y las aplicaciones con recursos limitados, incluidas las aplicaciones de escritorio, los videojuegos, los servidores (por ejemplo, el comercio electrónico, la búsqueda web o las bases de datos) y las aplicaciones críticas para el rendimiento (ej., conmutadores telefónicos o sondas espaciales).

C++ está estandarizado por la Organización Internacional de Normalización (ISO), con la última versión estándar ratificada y publicada por ISO en diciembre de 2020 como ISO/IEC 14882:2020 (conocido informalmente como C++ 20). El lenguaje de programación C++ se estandarizó inicialmente en 1998 como ISO/IEC 14882:1998, que luego fue modificado por C++03, C++11, C++14 y C++17. estándares El estándar C++20 actual los reemplaza con nuevas funciones y una biblioteca estándar ampliada. Antes de la estandarización inicial en 1998, C++ fue desarrollado por Stroustrup en Bell Labs desde 1979 como una extensión del lenguaje C; quería un lenguaje eficiente y flexible similar a C que también proporcionara funciones de alto nivel para la organización de programas. Desde 2012, C++ ha estado en un programa de lanzamiento de tres años con C++ 23 como el próximo estándar planificado.

Historia

Bjarne Stroustrup, el creador de C++, en su oficina de AT Tomás New Jersey, c. 2000

En 1979, Bjarne Stroustrup, un informático danés, comenzó a trabajar en "C con clases", el predecesor de C++. La motivación para crear un nuevo lenguaje se originó a partir de la experiencia de programación de Stroustrup para su tesis doctoral. Stroustrup descubrió que Simula tenía funciones que eran muy útiles para el desarrollo de software de gran tamaño, pero el lenguaje era demasiado lento para un uso práctico, mientras que BCPL era rápido pero de un nivel demasiado bajo para ser adecuado para el desarrollo de software de gran tamaño. Cuando Stroustrup comenzó a trabajar en AT&T Bell Labs, tuvo el problema de analizar el kernel de UNIX con respecto a la computación distribuida. Recordando su experiencia de doctorado, Stroustrup se dispuso a mejorar el lenguaje C con funciones similares a Simula. Se eligió C porque era de propósito general, rápido, portátil y ampliamente utilizado. Además de las influencias de C y Simula, otros lenguajes también influyeron en este nuevo lenguaje, incluidos ALGOL 68, Ada, CLU y ML.

Inicialmente, Stroustrup's 'C with Classes' Se agregaron funciones al compilador de C, Cpre, incluidas clases, clases derivadas, tipificación fuerte, argumentos en línea y predeterminados.

Una prueba sobre las características de C+11 en París en 2015

En 1982, Stroustrup comenzó a desarrollar un sucesor de C con Classes, al que denominó "C++" (++ siendo el operador de incremento en C) después de pasar por varios otros nombres. Se agregaron nuevas funciones, incluidas funciones virtuales, nombre de función y sobrecarga de operadores, referencias, constantes, asignación de memoria de almacenamiento libre con seguridad de tipo (nuevo/eliminar), verificación de tipo mejorada y comentarios de una sola línea de estilo BCPL con dos barras diagonales (//). Además, Stroustrup desarrolló un nuevo compilador independiente para C++, Cfront.

En 1984, Stroustrup implementó la primera biblioteca de flujo de entrada/salida. La idea de proporcionar un operador de salida en lugar de una función de salida con nombre fue sugerida por Doug McIlroy (quien previamente había sugerido tuberías Unix).

En 1985 se lanzó la primera edición de El lenguaje de programación C++, que se convirtió en la referencia definitiva del lenguaje, ya que aún no existía un estándar oficial. La primera implementación comercial de C++ se lanzó en octubre del mismo año.

En 1989, se lanzó C++ 2.0, seguido de la segunda edición actualizada de El lenguaje de programación C++ en 1991. Las nuevas características en 2.0 incluyeron herencia múltiple, clases abstractas, funciones de miembros estáticos, funciones de miembros const. y miembros protegidos. En 1990, se publicó El manual de referencia de C++ anotado. Este trabajo se convirtió en la base para el futuro estándar. Las adiciones de funciones posteriores incluyeron plantillas, excepciones, espacios de nombres, nuevas conversiones y un tipo booleano.

En 1998, se lanzó C++98, estandarizando el lenguaje, y se lanzó una actualización menor (C++03) en 2003.

Después de C++98, C++ evolucionó con relativa lentitud hasta que, en 2011, se lanzó el estándar C++11, que agregó numerosas características nuevas, amplió aún más la biblioteca estándar y brindó más facilidades a los programadores de C++. Después de una actualización menor de C++14 lanzada en diciembre de 2014, se introdujeron varias adiciones nuevas en C++17. Después de finalizarse en febrero de 2020, se aprobó un borrador del estándar C++ 20 el 4 de septiembre de 2020 y se publicó oficialmente el 15 de diciembre de 2020.

El 3 de enero de 2018, Stroustrup fue anunciado como el ganador de 2018 del Premio Charles Stark Draper de Ingeniería, "por conceptualizar y desarrollar el lenguaje de programación C++".

En diciembre de 2022, C++ ocupó el tercer lugar en el índice TIOBE, superando a Java por primera vez en la historia del índice. Ocupa el tercer lugar, después de Python y C.

Etimología

Según Stroustrup, "el nombre significa la naturaleza evolutiva de los cambios de C". Este nombre se le atribuye a Rick Mascitti (mediados de 1983) y se usó por primera vez en diciembre de 1983. Cuando se le preguntó informalmente a Mascitti en 1992 sobre el nombre, indicó que se lo dio con un espíritu irónico. El nombre proviene de C's ++ Operador (que incrementa el valor de una variable) y una convención de nomenclatura común de usar "+" para indicar un programa informático mejorado.

Durante el período de desarrollo de C++, el lenguaje se denominaba "nuevo C" y "C con Clases" antes de adquirir su nombre definitivo.

Filosofía

A lo largo de la vida de C++, su desarrollo y evolución se ha guiado por un conjunto de principios:

  • Debe ser impulsado por problemas reales y sus características deben ser inmediatamente útiles en los programas del mundo real.
  • Cada característica debe ser implementable (con una manera razonablemente obvia de hacerlo).
  • Los programadores deben ser libres para elegir su propio estilo de programación, y ese estilo debe ser apoyado por C++.
  • Permitir una característica útil es más importante que prevenir cada posible uso indebido de C++.
  • Debe proporcionar instalaciones para organizar programas en partes separadas y bien definidas, y proporcionar instalaciones para combinar partes desarrolladas por separado.
  • No hay violaciones implícitas del sistema de tipo (pero permiten violaciones explícitas; es decir, las solicitadas explícitamente por el programador).
  • Los tipos creados por el usuario necesitan tener el mismo soporte y rendimiento que los tipos incorporados.
  • Las características no utilizadas no deben afectar negativamente a los ejecutables creados (por ejemplo, en menor rendimiento).
  • No debe haber ningún idioma bajo C++ (excepto el lenguaje de montaje).
  • C++ debe trabajar junto con otros idiomas de programación existentes, en lugar de fomentar su propio entorno de programación separado e incompatible.
  • Si se desconoce la intención del programador, permita al programador especificarla mediante el control manual.

Estandarización

Escena durante la reunión del comité de normas C++ en Estocolmo en 1996
C+++
Año C++ Estándar Nombre oficioso
1998 ISO/IEC 14882:1998C+98
2003 ISO/IEC 14882:2003C++03
2011 ISO/IEC 14882:2011C++11, C+0x
2014 ISO/IEC 14882:2014C++14, C++1y
2017 ISO/IEC 14882:2017C++17, C+1z
2020 ISO/IEC 14882:2020C++20, C+2a
2023 C++23

C++ está estandarizado por un grupo de trabajo de ISO conocido como JTC1/SC22/WG21. Hasta el momento, ha publicado seis revisiones del estándar C++ y actualmente está trabajando en la próxima revisión, C++23.

En 1998, el grupo de trabajo de ISO estandarizó C++ por primera vez como ISO/IEC 14882:1998, que se conoce informalmente como C++98. En 2003, publicó una nueva versión del estándar C++ denominada ISO/IEC 14882:2003, que solucionó los problemas identificados en C++98.

La próxima revisión importante del estándar se denominó informalmente "C++0x", pero no se publicó hasta 2011. C++11 (14882:2011) incluyó muchas adiciones tanto al lenguaje central y la biblioteca estándar.

En 2014, se lanzó C++14 (también conocido como C++1y) como una pequeña extensión de C++11, que presenta principalmente correcciones de errores y pequeñas mejoras. El borrador de los procedimientos de votación estándar internacional se completó a mediados de agosto de 2014.

Después de C++14, el comité ISO C++ completó una revisión importante de C++17, informalmente conocida como C++1z, a mediados de julio de 2017 y fue aprobada y publicada en diciembre de 2017.

Como parte del proceso de estandarización, ISO también publica especificaciones e informes técnicos:

  • ISO/IEC TR 18015:2006 sobre el uso de C++ en sistemas integrados y sobre las implicaciones de rendimiento de las funciones de lenguaje y biblioteca C++,
  • ISO/IEC TR 19768:2007 (también conocido como C++ Informe Técnico 1) sobre extensiones de biblioteca integradas principalmente en C++11,
  • ISO/IEC TR 29124:2010 sobre funciones matemáticas especiales, integradas en C++17
  • ISO/IEC TR 24733:2011 sobre aritmética de punto flotante decimal,
  • ISO/IEC TS 18822:2015 en la biblioteca estándar del sistema de archivos, integrada en C++17
  • ISO/IEC TS 19570:2015 en versiones paralelas de los algoritmos de biblioteca estándar, integrados en C++17
  • ISO/IEC TS 19841:2015 en memoria transaccional del software,
  • ISO/IEC TS 19568:2015 sobre un nuevo conjunto de extensiones de biblioteca, algunas de las cuales ya están integradas en C++17,
  • ISO/IEC TS 19217:2015 sobre los conceptos C++, integrados en C++20
  • ISO/IEC TS 19571:2016 sobre las extensiones de biblioteca para la concurrencia, algunas de las cuales ya están integradas en C++20
  • ISO/IEC TS 19568:2017 sobre un nuevo conjunto de extensiones de biblioteca de uso general
  • ISO/IEC TS 21425:2017 en las extensiones de biblioteca para rangos, integradas en C++20
  • ISO/IEC TS 22277:2017 sobre coroutines, integrado en C++20
  • ISO/IEC TS 19216:2018 en la biblioteca de redes
  • ISO/IEC TS 21544:2018 sobre módulos integrados en C++20
  • ISO/IEC TS 19570:2018 sobre un nuevo conjunto de extensiones de biblioteca para el paralelismo
  • ISO/IEC TS 23619:2021 sobre nuevas extensiones de reflexión

Hay más especificaciones técnicas en desarrollo y pendientes de aprobación, incluido un nuevo conjunto de extensiones de simultaneidad.

Idioma

El lenguaje C++ tiene dos componentes principales: una asignación directa de funciones de hardware proporcionada principalmente por el subconjunto C y abstracciones sin sobrecarga basadas en esas asignaciones. Stroustrup describe C++ como "un lenguaje de programación de abstracción liviano [diseñado] para construir y usar abstracciones eficientes y elegantes"; y "ofrecer acceso al hardware y abstracción es la base de C++. Hacerlo de manera eficiente es lo que lo distingue de otros lenguajes."

C++ hereda la mayor parte de la sintaxis de C. La siguiente es la versión de Bjarne Stroustrup del programa Hello world que utiliza la función de transmisión de la biblioteca estándar de C++ para escribir un mensaje en la salida estándar:

#include ■iostreamint principal(){} std::Cout .. "¡Hola, mundo!n";}

Almacenamiento de objetos

Al igual que en C, C++ admite cuatro tipos de administración de memoria: objetos de duración de almacenamiento estático, objetos de duración de almacenamiento de subprocesos, objetos de duración de almacenamiento automático y objetos de duración de almacenamiento dinámico.

Objetos de duración de almacenamiento estático

Los objetos de duración de almacenamiento estático se crean antes de que se ingrese main() (consulte las excepciones a continuación) y se destruyen en orden inverso a la creación después de que sale main(). El orden exacto de creación no está especificado por el estándar (aunque hay algunas reglas definidas a continuación) para permitir a las implementaciones cierta libertad en cómo organizar su implementación. Más formalmente, los objetos de este tipo tienen una vida útil que "durará mientras dure el programa".

Los objetos de duración de almacenamiento estático se inicializan en dos fases. Primero, "inicialización estática" se realiza, y solo después de realizar toda la inicialización estática, se realiza la "inicialización dinámica" es interpretado. En la inicialización estática, todos los objetos se inicializan primero con ceros; después de eso, todos los objetos que tienen una fase de inicialización constante se inicializan con la expresión constante (es decir, las variables se inicializan con un literal o constexpr). Aunque no se especifica en el estándar, la fase de inicialización estática puede completarse en tiempo de compilación y guardarse en la partición de datos del ejecutable. La inicialización dinámica involucra toda la inicialización de objetos realizada a través de un constructor o llamada de función (a menos que la función esté marcada con constexpr, en C++11). El orden de inicialización dinámica se define como el orden de declaración dentro de la unidad de compilación (es decir, el mismo archivo). No se proporcionan garantías sobre el orden de inicialización entre las unidades de compilación.

Objetos de duración de almacenamiento de subprocesos

Las variables de este tipo son muy similares a los objetos de duración de almacenamiento estático. La principal diferencia es que el tiempo de creación es justo antes de la creación del subproceso y la destrucción se realiza después de que se ha unido el subproceso.

Objetos de duración de almacenamiento automático

Los tipos de variables más comunes en C++ son variables locales dentro de una función o bloque y variables temporales. La característica común de las variables automáticas es que tienen un tiempo de vida limitado al alcance de la variable. Se crean y potencialmente se inicializan en el punto de declaración (consulte los detalles a continuación) y se destruyen en el orden inverso de creación cuando se abandona el alcance. Esto se implementa mediante la asignación en la pila.

Las variables locales se crean cuando el punto de ejecución pasa el punto de declaración. Si la variable tiene un constructor o inicializador, este se usa para definir el estado inicial del objeto. Las variables locales se destruyen cuando se cierra el bloque local o la función en la que están declaradas. Los destructores de C++ para las variables locales se llaman al final de la vida útil del objeto, lo que permite una disciplina para la gestión automática de recursos denominada RAII, que se usa ampliamente en C++.

Las variables miembro se crean cuando se crea el objeto principal. Los miembros de la matriz se inicializan desde 0 hasta el último miembro de la matriz en orden. Las variables miembro se destruyen cuando el objeto principal se destruye en el orden inverso al de creación. es decir, si el padre es un "objeto automático" luego será destruido cuando salga del alcance, lo que desencadena la destrucción de todos sus miembros.

Las variables temporales se crean como resultado de la evaluación de la expresión y se destruyen cuando la instrucción que contiene la expresión se ha evaluado por completo (normalmente en el ; al final de una instrucción).

Objetos de duración de almacenamiento dinámico

Estos objetos tienen una vida útil dinámica y se pueden crear directamente con una llamada a nuevo y destruido explícitamente con una llamada a eliminar. C++ también admite malloc y free, de C, pero estos no son compatibles con nuevo y eliminar. Uso de nuevo devuelve una dirección a la memoria asignada. Las Directrices básicas de C++ desaconsejan el uso de new directamente para crear objetos dinámicos a favor de punteros inteligentes a través de hacer_único<T> para propiedad única y hacer_compartido<T > para propiedad múltiple contada por referencia, que se introdujeron en C++11.

Plantillas

Las plantillas de C++ permiten la programación genérica. C++ admite plantillas de funciones, clases, alias y variables. Las plantillas se pueden parametrizar por tipos, constantes de tiempo de compilación y otras plantillas. Las plantillas se implementan mediante instanciación en tiempo de compilación. Para instanciar una plantilla, los compiladores sustituyen argumentos específicos por los parámetros de una plantilla para generar una función concreta o instancia de clase. Algunas sustituciones no son posibles; estos se eliminan mediante una política de resolución de sobrecarga descrita por la frase "La falla de sustitución no es un error" (SFINAE). Las plantillas son una herramienta poderosa que se puede usar para programación genérica, metaprogramación de plantillas y optimización de código, pero este poder implica un costo. El uso de plantillas puede aumentar el tamaño del código, porque cada instancia de plantilla produce una copia del código de plantilla: uno para cada conjunto de argumentos de plantilla, sin embargo, esta es la misma cantidad de código o menor que se generaría si el código se escribiera a mano. Esto contrasta con los genéricos en tiempo de ejecución que se ven en otros lenguajes (por ejemplo, Java), donde en el momento de la compilación se borra el tipo y se conserva un solo cuerpo de plantilla.

Las plantillas son diferentes de las macros: mientras que estas dos características del lenguaje en tiempo de compilación permiten la compilación condicional, las plantillas no están restringidas a la sustitución léxica. Las plantillas conocen la semántica y el sistema de tipos de su lenguaje complementario, así como todas las definiciones de tipos en tiempo de compilación, y pueden realizar operaciones de alto nivel, incluido el control de flujo programático basado en la evaluación de parámetros estrictamente verificados. Las macros tienen la capacidad de controlar condicionalmente la compilación en función de criterios predeterminados, pero no pueden crear instancias de nuevos tipos, repetir o realizar una evaluación de tipos y, de hecho, se limitan a la sustitución de texto previa a la compilación y la inclusión/exclusión de texto. En otras palabras, las macros pueden controlar el flujo de compilación en función de símbolos predefinidos, pero no pueden, a diferencia de las plantillas, crear instancias de nuevos símbolos de forma independiente. Las plantillas son una herramienta para el polimorfismo estático (ver más abajo) y la programación genérica.

Además, las plantillas son un mecanismo de tiempo de compilación en C++ que es Turing-completo, lo que significa que cualquier cálculo expresable por un programa de computadora puede ser calculado, de alguna forma, por un metaprograma de plantilla antes del tiempo de ejecución.

En resumen, una plantilla es una función o clase parametrizada en tiempo de compilación escrita sin conocimiento de los argumentos específicos utilizados para instanciarla. Después de la creación de instancias, el código resultante es equivalente al código escrito específicamente para los argumentos pasados. De esta manera, las plantillas proporcionan una forma de desacoplar aspectos genéricos y ampliamente aplicables de funciones y clases (codificados en plantillas) de aspectos específicos (codificados en parámetros de plantilla) sin sacrificar el rendimiento debido a la abstracción.

Objetos

C++ introduce funciones de programación orientada a objetos (OOP) en C. Ofrece clases, que proporcionan las cuatro funciones comúnmente presentes en los lenguajes OOP (y algunos no OOP): abstracción, encapsulación, herencia y polimorfismo. Una característica distintiva de las clases de C++ en comparación con las clases de otros lenguajes de programación es la compatibilidad con destructores deterministas, que a su vez brindan compatibilidad con el concepto de adquisición de recursos es inicialización (RAII).

Encapsulación

La encapsulación consiste en ocultar información para garantizar que las estructuras de datos y los operadores se utilicen según lo previsto y para que el modelo de uso sea más evidente para el desarrollador. C++ proporciona la capacidad de definir clases y funciones como sus principales mecanismos de encapsulación. Dentro de una clase, los miembros se pueden declarar como públicos, protegidos o privados para hacer cumplir explícitamente la encapsulación. Un miembro público de la clase es accesible para cualquier función. Un miembro privado solo es accesible para las funciones que son miembros de esa clase y para las funciones y clases a las que la clase ("amigos") les otorgó explícitamente permiso de acceso. Un miembro protegido es accesible para los miembros de las clases que heredan de la clase además de la clase misma y sus amigos.

El principio orientado a objetos asegura la encapsulación de todas y solo las funciones que acceden a la representación interna de un tipo. C++ admite este principio a través de funciones miembro y funciones amigas, pero no lo aplica. Los programadores pueden declarar partes o la totalidad de la representación de un tipo como pública, y se les permite hacer que las entidades públicas no formen parte de la representación de un tipo. Por lo tanto, C++ admite no solo la programación orientada a objetos, sino también otros paradigmas de descomposición, como la programación modular.

En general, se considera una buena práctica hacer que todos los datos sean privados o protegidos, y hacer públicas solo aquellas funciones que forman parte de una interfaz mínima para los usuarios de la clase. Esto puede ocultar los detalles de la implementación de los datos, lo que permite al diseñador cambiar fundamentalmente la implementación sin cambiar la interfaz de ninguna manera.

Herencia

La herencia permite que un tipo de datos adquiera propiedades de otros tipos de datos. La herencia de una clase base puede declararse como pública, protegida o privada. Este especificador de acceso determina si las clases derivadas y no relacionadas pueden acceder a los miembros protegidos y públicos heredados de la clase base. Sólo la herencia pública corresponde a lo que normalmente se entiende por "herencia". Las otras dos formas se utilizan con mucha menos frecuencia. Si se omite el especificador de acceso, una "clase" hereda de forma privada, mientras que una "estructura" hereda públicamente. Las clases base pueden declararse como virtuales; esto se llama herencia virtual. La herencia virtual garantiza que solo exista una instancia de una clase base en el gráfico de herencia, lo que evita algunos de los problemas de ambigüedad de la herencia múltiple.

La herencia múltiple es una característica de C++ que permite que una clase se derive de más de una clase base; esto permite relaciones de herencia más elaboradas. Por ejemplo, un "Gato volador" la clase puede heredar tanto de "Cat" y 'Mamífero volador'. Algunos otros lenguajes, como C# o Java, logran algo similar (aunque más limitado) al permitir la herencia de múltiples interfaces al tiempo que restringen el número de clases base a una (las interfaces, a diferencia de las clases, brindan solo declaraciones de funciones miembro, sin implementación o miembro). datos). Una interfaz como en C# y Java se puede definir en C++ como una clase que contiene solo funciones virtuales puras, a menudo conocida como clase base abstracta o "ABC". Las funciones miembro de tal clase base abstracta normalmente se definen explícitamente en la clase derivada, no se heredan implícitamente. La herencia virtual de C++ exhibe una característica de resolución de ambigüedad llamada dominancia.

Operadores y sobrecarga de operadores

Operadores que no pueden ser sobrecargados
Operador Signatura
Scope resolution operator ::
Operador condicional ?:
operador .
Operador de selección de miembros .*
"Tamaño" operador sizeof
operador "tipoid" typeid

C++ proporciona más de 35 operadores, que cubren aritmética básica, manipulación de bits, direccionamiento indirecto, comparaciones, operaciones lógicas y otros. Casi todos los operadores se pueden sobrecargar para los tipos definidos por el usuario, con algunas excepciones notables, como el acceso de miembros (. y .*), así como el operador condicional. El amplio conjunto de operadores sobrecargables es fundamental para hacer que los tipos definidos por el usuario en C++ parezcan tipos integrados.

Los operadores sobrecargables también son una parte esencial de muchas técnicas avanzadas de programación en C++, como los punteros inteligentes. La sobrecarga de un operador no cambia la precedencia de los cálculos que involucran al operador, ni cambia la cantidad de operandos que utiliza el operador (sin embargo, el operador puede ignorar cualquier operando, aunque se evaluará antes de la ejecución). Sobrecargado "&&" y "||" los operadores pierden su propiedad de evaluación de cortocircuito.

Polimorfismo

El polimorfismo permite una interfaz común para muchas implementaciones y para que los objetos actúen de manera diferente en diferentes circunstancias.

C++ admite varios tipos de polimorfismos estáticos (resueltos en tiempo de compilación) y dinámicos (resueltos en tiempo de ejecución), compatibles con las características del lenguaje descritas anteriormente. El polimorfismo en tiempo de compilación no permite ciertas decisiones en tiempo de ejecución, mientras que el polimorfismo en tiempo de ejecución normalmente incurre en una penalización de rendimiento.

Polimorfismo estático

La sobrecarga de funciones permite que los programas declaren múltiples funciones con el mismo nombre pero con diferentes argumentos (es decir, polimorfismo ad hoc). Las funciones se distinguen por el número o tipos de sus parámetros formales. Así, el mismo nombre de función puede hacer referencia a diferentes funciones según el contexto en el que se utilice. El tipo devuelto por la función no se usa para distinguir funciones sobrecargadas y diferentes tipos de devolución darían como resultado un mensaje de error en tiempo de compilación.

Al declarar una función, un programador puede especificar un valor predeterminado para uno o más parámetros. Hacerlo permite que los parámetros con valores predeterminados se omitan opcionalmente cuando se llama a la función, en cuyo caso se utilizarán los argumentos predeterminados. Cuando se llama a una función con menos argumentos que los parámetros declarados, los argumentos explícitos se comparan con los parámetros en orden de izquierda a derecha, y a los parámetros no coincidentes al final de la lista de parámetros se les asignan sus argumentos predeterminados. En muchos casos, es preferible especificar argumentos predeterminados en una sola declaración de función que proporcionar definiciones de funciones sobrecargadas con diferentes números de parámetros.

Las plantillas en C++ proporcionan un mecanismo sofisticado para escribir código polimórfico genérico (es decir, polimorfismo paramétrico). En particular, a través del patrón de plantilla curiosamente recurrente, es posible implementar una forma de polimorfismo estático que imita de cerca la sintaxis para anular funciones virtuales. Debido a que las plantillas de C++ son compatibles con tipos y completas de Turing, también se pueden usar para permitir que el compilador resuelva condicionales recursivos y genere programas sustanciales a través de la metaprogramación de plantillas. Contrariamente a algunas opiniones, el código de plantilla no generará un código masivo después de la compilación con la configuración adecuada del compilador.

Polimorfismo dinámico

Herencia

Los punteros de variable y las referencias a un tipo de clase base en C++ también pueden hacer referencia a objetos de cualquier clase derivada de ese tipo. Esto permite que las matrices y otros tipos de contenedores contengan punteros a objetos de diferentes tipos (las referencias no se pueden mantener directamente en los contenedores). Esto permite el polimorfismo dinámico (en tiempo de ejecución), donde los objetos referidos pueden comportarse de manera diferente, dependiendo de sus tipos (reales, derivados).

C++ también proporciona la clase dynamic_cast, que permite que el código intente convertir de forma segura un objeto, a través de una referencia/puntero base, a un tipo más derivado: downcasting. El intento es necesario ya que a menudo uno no sabe a qué tipo derivado se hace referencia. (Upcasting, conversión a un tipo más general, siempre se puede verificar/realizar en tiempo de compilación a través del directorio static_cast, ya que las clases ancestrales se especifican en la interfaz de la clase derivada, visible para todas las personas que llaman). dynamic_cast se basa en la información de tipo en tiempo de ejecución (RTTI), metadatos en el programa que permite diferenciar tipos y sus relaciones. Si un dynamic_cast a un puntero falla, el resultado es el nullptr constante, mientras que si el destino es una referencia (que no puede ser nula), la conversión genera una excepción. Los objetos conocidos por ser de cierto tipo derivado se pueden convertir a eso con static_cast, sin pasar por RTTI y la verificación de tipo segura en tiempo de ejecución de dynamic_cast, entonces esto debería ser se usa solo si el programador está muy seguro de que el elenco es, y siempre será, válido.

Funciones de miembros virtuales

Por lo general, cuando una función en una clase derivada anula una función en una clase base, la función a llamar está determinada por el tipo de objeto. Una función determinada se anula cuando no existe diferencia en el número o tipo de parámetros entre dos o más definiciones de esa función. Por lo tanto, en tiempo de compilación, puede que no sea posible determinar el tipo de objeto y, por lo tanto, la función correcta para llamar, dado solo un puntero de clase base; por lo tanto, la decisión se pospone hasta el tiempo de ejecución. Esto se llama envío dinámico. Las funciones miembro virtuales o métodos permiten llamar a la implementación más específica de la función, de acuerdo con el tipo de tiempo de ejecución real del objeto. En las implementaciones de C++, esto se suele hacer mediante tablas de funciones virtuales. Si se conoce el tipo de objeto, esto se puede omitir anteponiendo un nombre de clase completo antes de la llamada a la función, pero en general, las llamadas a funciones virtuales se resuelven en tiempo de ejecución.

Además de las funciones miembro estándar, las sobrecargas de operadores y los destructores pueden ser virtuales. Una regla inexacta basada en la experiencia práctica establece que si alguna función en la clase es virtual, el destructor también debería serlo. Como el tipo de un objeto en su creación se conoce en tiempo de compilación, los constructores y, por extensión, los constructores de copias, no pueden ser virtuales. No obstante, puede surgir una situación en la que sea necesario crear una copia de un objeto cuando se pasa un puntero a un objeto derivado como puntero a un objeto base. En tal caso, una solución común es crear un clon() (o similar) función virtual que crea y devuelve una copia de la clase derivada cuando se le llama.

Una función miembro también se puede convertir en "virtual pura" agregándolo con = 0 después del paréntesis de cierre y antes del punto y coma. Una clase que contiene una función virtual pura se denomina clase abstracta. Los objetos no se pueden crear a partir de una clase abstracta; sólo pueden derivarse de. Cualquier clase derivada hereda la función virtual como pura y debe proporcionar una definición no pura de ella (y todas las demás funciones virtuales puras) antes de que se puedan crear objetos de la clase derivada. Un programa que intenta crear un objeto de una clase con una función de miembro virtual pura o una función de miembro virtual pura heredada está mal formado.

Expresiones lambda

C++ proporciona soporte para funciones anónimas, también conocidas como expresiones lambda, con el siguiente formato:

[captura]parámetros) - return_type {} función_cuerpo }

Desde C++20, la palabra clave template es opcional para los parámetros de plantilla de las expresiones lambda:

[captura].template_parameters()parámetros) - return_type {} función_cuerpo }

Si la lambda no toma parámetros y no se utiliza ningún tipo de retorno u otros especificadores, se puede omitir el (), es decir,

[captura] {} función_cuerpo }

El tipo de retorno de una expresión lambda se puede inferir automáticamente, si es posible, por ejemplo:

[](int x, int Sí.) {} retorno x + Sí.; } // inferido[](int x, int Sí.) - int {} retorno x + Sí.; } //

El [capture] admite la definición de cierres. Tales expresiones lambda se definen en el estándar como azúcar sintáctico para un objeto de función sin nombre.

Manejo de excepciones

El manejo de excepciones se usa para comunicar la existencia de un problema o error en tiempo de ejecución desde donde se detectó hasta donde se puede manejar el problema. Permite que esto se haga de manera uniforme y separada del código principal, mientras detecta todos los errores. Si se produce un error, se lanza (provoca) una excepción, que luego es detectada por el controlador de excepciones adecuado más cercano. La excepción hace que se salga del ámbito actual, y también de cada ámbito externo (propagación) hasta que se encuentre un controlador adecuado, llamando a su vez a los destructores de cualquier objeto en estos ámbitos cerrados. Al mismo tiempo, se presenta una excepción como un objeto que lleva los datos sobre el problema detectado.

Algunas guías de estilo de C++, como Google's, LLVM's y Qt's, prohíben el uso de excepciones.

El código que causa la excepción se coloca dentro de un intentar bloque. Las excepciones se manejan en catch (los controladores); cada intentar puede tener múltiples controladores de excepciones, como se ve en el ejemplo a continuación.

#include ■iostream#include Identificador#include - No.int principal() {} Prueba {} std::vector.int vec{}3, 4, 3, 1}; int i{}vec.a()4)} // Lanza una excepción, pt::out_of_range (El índice para vec es de 0-3 no 1-4) } // Un manipulador de excepción, atrapa std:out_of_range, que es lanzado por vec.at(4) captura ()std::fuera de fuera "e) {} std::cerradura .. "Acceso a un elemento inexistente: " .. e.¿Qué?() .. 'n '; } // Para atrapar cualquier otra excepción estándar de la biblioteca (se derivan de std::excepción) captura ()std::excepción "e) {} std::cerradura .. "Excepción lanzada: " .. e.¿Qué?() .. 'n '; } // Buscar cualquier excepción no reconocida (es decir, las que no derivan de std::excepción) captura (...) {} std::cerradura .. "Algún error mortaln"; }}

También es posible generar excepciones a propósito, usando el throw palabra clave; estas excepciones se manejan de la manera habitual. En algunos casos, las excepciones no se pueden utilizar por razones técnicas. Un ejemplo de ello es un componente crítico de un sistema integrado, donde se debe garantizar que cada operación se complete dentro de un período de tiempo específico. Esto no se puede determinar con excepciones ya que no existen herramientas para determinar el tiempo máximo requerido para que se maneje una excepción.

A diferencia del manejo de señales, en el que la función de manejo se llama desde el punto de falla, el manejo de excepciones sale del alcance actual antes de que se ingrese el bloque catch, que puede estar ubicado en la función actual o en cualquiera de las llamadas de función anteriores actualmente en la pila.

Tipos enumerados

C++ tiene tipos de enumeración que se heredan directamente de C's y trabajan principalmente como estos, excepto que una enumeración es un tipo real en C++, dando una comprobación compilada adicional. También (como con las estructuras), el C++ enum palabra clave se combina automáticamente con una Tipodef, así que en lugar de nombrar el tipo enum name, simplemente nombrarlo name. Esto se puede simular en C usando un tipodef: typedef enum {Value1, Value2} name;

C++11 también proporciona un segundo tipo de enumeración, llamado enumeración. Estos son de tipo seguro: los enumeradores no se convierten implícitamente a un tipo entero. Entre otras cosas, esto permite que el streaming I/O se defina para el tipo de enumeración. Otra característica de enumeraciones de alcance es que los enumeradores no filtran, por lo que el uso requiere prefijo con el nombre de la enumeración (por ejemplo, Color::Red para el primer enumerador en el ejemplo que figura a continuación) using enum declaración (introducida en C++20) se ha utilizado para llevar a los enumeradores al alcance actual. Una enumeración abarcada se especifica en la frase enum class (o enum struct). Por ejemplo:

enum clase Color {}Rojo, Verde, Azul};

El tipo subyacente de una enumeración es un tipo integral definido por la implementación que es lo suficientemente grande para mantener todos los valores enumerados; no tiene que ser el tipo más pequeño posible. El tipo subyacente se puede especificar directamente, lo que permite "declaraciones futuras" de enumeraciones:

enum clase Color : largo {}Rojo, Verde, Azul}; // debe caber en tamaño y diseño de memoria del tipo 'long 'enum clase Formas : char; // Declaración de avance. Si después hay valores definidos que no encajan en 'char' es un error.

Biblioteca estándar

El borrador "Working Paper" estándar que se aprobó como C+98; la mitad de su tamaño se dedicó a la C++ Biblioteca Estándar.

El estándar C++ consta de dos partes: el lenguaje central y la biblioteca estándar. Los programadores de C++ esperan lo último en cada implementación importante de C++; incluye tipos agregados (vectores, listas, mapas, conjuntos, colas, pilas, matrices, tuplas), algoritmos (find, for_each, binary_search, random_shuffle, etc.), recursos de entrada/salida (iostream, para leer y escribir en el consola y archivos), biblioteca de sistemas de archivos, soporte de localización, punteros inteligentes para la administración automática de memoria, soporte de expresiones regulares, biblioteca de subprocesos múltiples, soporte atómico (permitiendo que una variable sea leída o escrita en una variable como máximo por un subproceso a la vez sin ninguna conexión externa). sincronización), utilidades de tiempo (medición, obtención de la hora actual, etc.), un sistema para convertir informes de errores que no usan excepciones de C++ en excepciones de C++, un generador de números aleatorios y una versión ligeramente modificada de la biblioteca estándar de C (para que cumpla con el sistema de tipos de C++).

Gran parte de la biblioteca de C++ se basa en la biblioteca de plantillas estándar (STL). Las herramientas útiles proporcionadas por STL incluyen contenedores como colecciones de objetos (como vectores y listas), iteradores que brindan acceso similar a una matriz a los contenedores y algoritmos que realizan operaciones como búsqueda y clasificación.

Además, se proporcionan (múltiples) mapas (matrices asociativas) y (múltiples) conjuntos, todos los cuales exportan interfaces compatibles. Por lo tanto, usando plantillas es posible escribir algoritmos genéricos que funcionen con cualquier contenedor o en cualquier secuencia definida por iteradores. Al igual que en C, se accede a las funciones de la biblioteca mediante el # include directiva para incluir un encabezado estándar. La biblioteca estándar de C++ proporciona 105 encabezados estándar, de los cuales 27 están en desuso.

El estándar incorpora el STL que fue diseñado originalmente por Alexander Stepanov, quien experimentó con algoritmos y contenedores genéricos durante muchos años. Cuando comenzó con C ++, finalmente encontró un lenguaje en el que era posible crear algoritmos genéricos (por ejemplo, clasificación STL) que funcionan incluso mejor que, por ejemplo, la biblioteca estándar de C qsort, gracias a las características de C ++ como usar en línea y compilar. enlace de tiempo en lugar de punteros de función. El estándar no se refiere a él como "STL", ya que es simplemente una parte de la biblioteca estándar, pero el término todavía se usa ampliamente para distinguirlo del resto de la biblioteca estándar (flujos de entrada/salida)., internacionalización, diagnósticos, el subconjunto de la biblioteca C, etc.).

La mayoría de los compiladores de C++, y todos los principales, proporcionan una implementación conforme a los estándares de la biblioteca estándar de C++.

Directrices básicas de C++

Las Directrices básicas de C++ son una iniciativa dirigida por Bjarne Stroustrup, el inventor de C++, y Herb Sutter, el convocante y presidente del Grupo de trabajo de C++ ISO, para ayudar a los programadores a escribir 'C++ moderno' mediante el uso de mejores prácticas para los estándares de lenguaje C++ 11 y posteriores, y para ayudar a los desarrolladores de compiladores y herramientas de verificación estática a crear reglas para detectar malas prácticas de programación.

El objetivo principal es escribir C++ seguro de tipos y recursos de forma eficaz y coherente.

Las Directrices principales se anunciaron en el discurso inaugural de la CPPCon 2015.

Las Pautas van acompañadas de la Biblioteca de soporte de pautas (GSL), una biblioteca de tipos y funciones solo de encabezado para implementar las Pautas principales y herramientas de verificación estática para hacer cumplir las reglas de las Pautas.

Compatibilidad

Para dar mayor libertad a los proveedores de compiladores, el comité de estándares de C++ decidió no dictar la implementación de la manipulación de nombres, el manejo de excepciones y otras características específicas de la implementación. La desventaja de esta decisión es que se espera que el código objeto producido por diferentes compiladores sea incompatible. Sin embargo, hubo intentos de estandarizar los compiladores para máquinas o sistemas operativos particulares (por ejemplo, C++ ABI), aunque ahora parecen haberse abandonado en gran medida.

Con C

A menudo se considera que C++ es un superconjunto de C, pero esto no es estrictamente cierto. La mayoría del código C puede compilarse fácilmente en C++, pero existen algunas diferencias que hacen que algunos códigos C válidos no sean válidos o se comporten de manera diferente en C++. Por ejemplo, C permite la conversión implícita de void* a otros tipos de punteros, pero C++ no (por razones de seguridad de tipos). Además, C++ define muchas palabras clave nuevas, como new y class, que pueden usarse como identificadores (por ejemplo, nombres de variables) en un programa C.

La revisión de 1999 del estándar C (C99) eliminó algunas incompatibilidades, que ahora admite funciones de C++, como comentarios de línea (//) y declaraciones mezcladas con código. Por otro lado, C99 introdujo una serie de características nuevas que C++ no admitía y que eran incompatibles o redundantes en C++, como arreglos de longitud variable, tipos de números complejos nativos (sin embargo, el std::complex en la biblioteca estándar de C++ proporciona una funcionalidad similar, aunque no es compatible con el código), inicializadores designados, literales compuestos y restringir palabra clave. Algunas de las funciones introducidas por C99 se incluyeron en la versión posterior del estándar C++, C++11 (de las que no eran redundantes). Sin embargo, el estándar C++11 introduce nuevas incompatibilidades, como no permitir la asignación de un literal de cadena a un puntero de carácter, que sigue siendo válido para C.

Para mezclar código C y C++, cualquier declaración o definición de función que deba ser llamada o utilizada tanto en C como en C++ debe declararse con enlace C colocándola dentro de un externo "C" {/*...*/} bloque. Es posible que dicha función no se base en características que dependan de la manipulación de nombres (es decir, la sobrecarga de funciones).

Crítica

A pesar de su adopción generalizada, algunos programadores notables han criticado el lenguaje C++, incluidos Linus Torvalds, Richard Stallman, Joshua Bloch, Ken Thompson y Donald Knuth.

Uno de los puntos más criticados de C++ es su complejidad percibida como lenguaje, con la crítica de que una gran cantidad de características no ortogonales en la práctica requieren restringir el código a un subconjunto de C++, evitando así los beneficios de legibilidad del estilo común y modismos.. Como lo expresó Joshua Bloch:

Creo que C++ fue empujado mucho más allá de su umbral de complejidad, y sin embargo hay mucha gente que lo programa. Pero lo que haces es obligar a la gente a subconectarlo. Así que casi todas las tiendas que conozco de que usa C++ dice, "Sí, estamos usando C+++ pero no estamos haciendo herencia de implementación múltiple y no estamos usando sobrecarga de operador". Sólo hay un montón de características que no vas a utilizar porque la complejidad del código resultante es demasiado alta. Y no creo que sea bueno cuando tienes que empezar a hacer eso. Pierdes esta portabilidad del programador donde todos pueden leer el código de los demás, lo que creo que es algo bueno.

Donald Knuth (1993, comentando sobre C++ preestandarizado), quien dijo de Edsger Dijkstra que "pensar en programar en C++" "lo enfermaría físicamente":

El problema que tengo hoy con ellos es que... C++ es demasiado complicado. De momento, es imposible para mí escribir código portátil que creo que trabajaría en muchos sistemas diferentes, a menos que evite todas las características exóticas. Cada vez que los diseñadores de lenguaje C++ tenían dos ideas competitivas sobre cómo deben resolver algún problema, dijeron "OK, los haremos ambos". Así que el lenguaje es demasiado barroco para mi gusto.

Ken Thompson, quien fue colega de Stroustrup en Bell Labs, da su evaluación:

Ciertamente tiene sus buenos puntos. Pero por grande creo que es un mal lenguaje. Hace muchas cosas bien y es sólo un montón de ideas que son mutuamente excluyentes. Todo el mundo que conozco, ya sea personal o corporativo, selecciona un subconjunto y estos subconjuntos son diferentes. Así que no es un buen lenguaje para transportar un algoritmo, decir, "lo escribí; aquí, tómalo". Es demasiado grande, demasiado complejo. Y obviamente es construido por un comité. Stroustrup hizo campaña durante años, años y años, mucho más allá de cualquier tipo de contribuciones técnicas que hizo al lenguaje, para conseguirlo adoptado y utilizado. Y él manejaba todos los comités de estándares con un látigo y una silla. Y dijo "no" a nadie. Puso todas las características en ese lenguaje que existió. No fue diseñado limpiamente, fue sólo la unión de todo lo que vino. Y creo que sufrió drásticamente por eso.

Sin embargo, Brian Kernighan, también colega de Bell Labs, cuestiona esta evaluación:

C++ ha sido enormemente influyente.... Mucha gente dice que C++ es demasiado grande y demasiado complicado, etc. etc. pero de hecho es un lenguaje muy poderoso y casi todo lo que está ahí está ahí por una razón muy sólida: no es alguien haciendo invención al azar, es en realidad la gente que trata de resolver problemas del mundo real. Ahora muchos de los programas que damos por sentado hoy, que sólo utilizamos, son programas C+++.

El propio Stroustrup comenta que la semántica de C++ es mucho más limpia que su sintaxis: "dentro de C++, hay un lenguaje mucho más pequeño y limpio que lucha por salir".

Otras quejas pueden incluir una falta de reflexión o recolección de elementos no utilizados, largos tiempos de compilación, percepción de que las características se arrastran y mensajes de error detallados, particularmente de la metaprogramación de plantillas.

Contenido relacionado

ASCII (revista)

ASCII era una revista de microcomputadoras publicada mensualmente en Japón, publicada por ASCII Corporation desde 1977. Estaba dirigida a usuarios...

USS Bon Homme Richard (CV-31)

USS Bon Homme Richard fue el decimocuarto de los 24 portaaviones de la clase Essex completados durante o poco después de la Segunda Guerra Mundial para los...

USS Washington (BB-56)

USS Washington fue el segundo y último miembro de la clase de acorazados rápidos de Carolina del Norte, el primer buque de este tipo construido para los...
Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save