Comparación de Java y C++

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar

Java y C++ son dos destacados lenguajes de programación orientados a objetos. Según muchas métricas de popularidad del lenguaje, los dos lenguajes han dominado el desarrollo de software orientado a objetos y de alto rendimiento durante gran parte del siglo XXI y, a menudo, se comparan y contrastan directamente. Java apareció unos 10 años después y su sintaxis estaba basada en C/C++.

Objetivos de diseño

Las diferencias entre los lenguajes de programación C++ y Java se pueden atribuir a su herencia, ya que tienen diferentes objetivos de diseño.

C++ fue diseñado para la programación de sistemas y aplicaciones (es decir, programación de infraestructura), extendiendo el lenguaje de programación procedimental C, que fue diseñado para una ejecución eficiente. A C, C ++ agregó soporte para programación orientada a objetos, manejo de excepciones, administración de recursos basada en la vida útil (RAII), programación genérica, metaprogramación de plantillas y la biblioteca estándar de C ++ que incluye contenedores y algoritmos genéricos (la biblioteca de plantillas estándar o STL), y muchas otras instalaciones de propósito general.

Java es un lenguaje de programación de propósito general, concurrente, basado en clases y orientado a objetos que está diseñado para minimizar las dependencias de implementación. Se basa en una máquina virtual Java para ser seguro y altamente portátil. Se incluye con una extensa biblioteca diseñada para proporcionar una abstracción completa de la plataforma subyacente. Java es un lenguaje orientado a objetos tipificado estáticamente que usa una sintaxis similar (pero incompatible con) C++. Incluye un sistema de documentación llamado Javadoc.

Los diferentes objetivos en el desarrollo de C++ y Java dieron como resultado diferentes principios y compensaciones de diseño entre los lenguajes. Las diferencias son las siguientes:

C++ Java
Extende C con programación orientada hacia objetos y programación genérica. El código C se puede utilizar correctamente. Fuertemente influenciada por la sintaxis C+/C.
Compatible con código fuente C, excepto por algunos casos de esquina. Proporciona la interfaz nativa Java y recientemente Java Native Access como una manera de llamar directamente C/C++.
Escribe una vez, compila en cualquier lugar (WOCA). Escribe una vez, corre por cualquier lugar (WORA/WORE).
Permite la programación procesal, programación funcional, programación orientada hacia objetos, programación genérica y metaprogramación de plantillas. Favorece una mezcla de paradigmas. Permite la programación procesal, la programación funcional (desde Java 8) y la programación genérica (desde Java 5), pero alienta firmemente el paradigma de programación orientado a objetos. Incluye soporte para crear lenguajes de scripting.
Corre como código de máquina ejecutable nativo para el conjunto(s) de instrucciones de destino. Corre en una máquina virtual.
Proporciona tipos de objetos y nombres de tipo. Permite la reflexión a través de la información tipo run-time (RTTI). Es reflexivo, permitiendo metaprogramación y generación dinámica de código en tiempo de ejecución.
Tiene múltiples estándares de compatibilidad binaria (comúnmente Microsoft (para el compilador MSVC) e Itanium/GNU (para casi todos los demás compiladores)). Tiene un estándar de compatibilidad binaria, multiplataforma para OS y compilador.
Verificación de límites automatizados opcionales (por ejemplo, el at() método en vector y string contenedores). Todas las operaciones deben ser comprobadas por todas las distribuciones compatibles de Java. HotSpot puede eliminar la comprobación de límites.
Apoyo aritmético no firmado. Nativo aritmético no firmado sin apoyo. Java 8 cambia algo de esto, pero los aspectos no son claros.
Límites mínimos estandarizados para todos los tipos numéricos, pero los tamaños reales están definidos en la implementación. Los tipos estandarizados están disponibles a través de la biblioteca estándar . Limitaciones y tamaños estandarizados de todos los tipos primitivos en todas las plataformas.
Los punteros, las referencias y el valor paso a paso son compatibles para todo tipo (primitivo o definido por el usuario). Todos los tipos (tipos primarios y tipos de referencia) siempre se pasan por valor.
La gestión de memoria se puede hacer manualmente a través de new / delete, automáticamente por alcance, o por punteros inteligentes. Apoya la destrucción determinista de objetos. Colección de basura ABI estandarizada en C++11, aunque los compiladores no son necesarios para implementar la recolección de basura. Colección de basura automática. Apoya un método de finalización no determinista, cuyo uso no se recomienda.
La gestión de los recursos puede hacerse manualmente o mediante la gestión automática de los recursos basados en la vida útil (RAII). Por lo general, la gestión de los recursos debe hacerse manualmente o automáticamente a través de los finalizadores, aunque generalmente se desalienta. Ha probado con recursos para la gestión automática de los recursos basados en el alcance (versión 7 en adelante).

También se puede hacer utilizando la API interna sun.misc.Unsafe pero ese uso está muy desalentado y será reemplazado por una API pública en una próxima versión Java.

Apoya clases, estructuras (tipos de estructura de datos pasivos) y sindicatos, y puede asignarlos en el montón o en la pila. Las clases se asignan en el montón. Java SE 6 optimiza con el análisis de escape para asignar algunos objetos en la pila.
Permite explícitamente los tipos dominantes, y algunas conversiones de estrechamiento implícito (para compatibilidad con C). Seguridad tipo rígida excepto para las conversiones de ampliación.
La Biblioteca Estándar C++ fue diseñada para tener un alcance y funciones limitados, pero incluye soporte de idiomas, diagnósticos, utilidades generales, cadenas, locales, contenedores, algoritmos, iteradores, numéricos, entrada/salida, generadores de números aleatorios, persiana de expresión regular, instalaciones de rosca, características tipo (para introspección de tipo estático) y Biblioteca C Estándar. La biblioteca Boost ofrece más funciones incluyendo la red I/O.

Existen numerosas bibliotecas de terceros para GUI y otras funciones como: Adaptive Communication Environment (ACE), Crypto++, varias bibliotecas XMPP Instant Messaging (IM), OpenLDAP, Qt, gtkmm.

La biblioteca estándar ha crecido con cada lanzamiento. En la versión 1.6, la biblioteca incluía soporte para locales, taladro, contenedores e iteradores, algoritmos, programación GUI (pero no utilizando el sistema GUI), gráficos, multi-telección, redes, seguridad de plataformas, introspección, carga de clase dinámica, bloqueo y no bloqueo I/O. Proporciona interfaces o clases de soporte para XML, XSLT, MIDI, conectividad de bases de datos, servicios de nombres (por ejemplo LDAP), criptografía, servicios de seguridad (por ejemplo Kerberos), servicios de impresión y servicios web. SWT ofreció una abstracción para GUIs específicas de plataforma, pero fue superada por JavaFX en las últimas versiones; permitiendo la aceleración de gráficos y UIs compatibles con CSS. Aunque no soporta ningún tipo de soporte de "mirado de plataforma nativa".
Operador sobrecarga para la mayoría de los operadores. Es muy recomendable conservar el significado (semántica). Los operarios no son prescindibles. El idioma anula + y += para la clase String.
herencia única y múltiple de clases, incluyendo la herencia virtual. Sólo apoya la herencia única de las clases.
Plantillas de tiempo completo. Permite Turing metaprogramación completa. Las genéticas se utilizan para lograr la parametrización básica de tipo, pero no se traducen de código fuente a código byte debido al uso de la borrado tipo por el compilador.
Puntos de función, objetos de función, lambdas (en C++11), e interfaces. Funciones referencias, objetos de función y lambdas fueron añadidos en Java 8. Clases (y interfaces, que son clases) pueden pasarse como referencias también a través de SomeClass.class y someObject.getClass().
No hay mecanismo estándar de documentación en línea. Existe un software de terceros (por ejemplo, Doxygen). Extensivo estándar de documentación Javadoc en todas las clases y métodos del sistema.
const palabra clave para definir variables inmutables y funciones miembro que no cambian el objeto. El const-ness se propaga como un medio para hacer cumplir, a tiempo de compilación, la corrección del código con respecto a la mutabilidad de los objetos (ver const-correctness). final proporciona una versión const, equivalente a type* const punteros para objetos y const para tipos primitivos. Inmutabilidad de los miembros del objeto alcanzada mediante interfaces de sólo lectura y encapsulación de objetos.
Apoya los goto declaración. Admite etiquetas con bucles y bloques de declaración. goto es una palabra clave reservada pero está marcada como "no usada" en la especificación Java.
El código fuente se puede escribir para ser multiplataforma (puede ser compilado para Windows, BSD, Linux, macOS, Solaris, etc., sin modificación) y escrito para usar funciones específicas de plataforma. Normalmente compilado en código de máquina nativo, debe ser recomendado para cada plataforma de destino. Compilado en Java bytecode para el JVM. El código Byte depende de la plataforma Java, pero normalmente es independiente de las características específicas del sistema operativo.

Características del idioma

Sintaxis

  • Sintaxis Java tiene una gramática sin contexto que puede ser analizada por un simple parser LALR. Parsing C++ es más complicado. Por ejemplo, Foo<1>(3); es una secuencia de comparaciones si Foo es una variable, pero crea un objeto si Foo es el nombre de una plantilla de clase.
  • C++ permite constantes, variables y funciones a nivel de espacio-nombre. En Java, tales entidades deben pertenecer a algún tipo dado, y por lo tanto deben definirse dentro de una definición de tipo, ya sea una clase o una interfaz.
  • En C++, los objetos son valores, mientras que en Java no lo son. Usos C++ valor semántico por defecto, mientras Java siempre utiliza referencia semántica. Para optar por semántica de referencia en C++, se puede utilizar un puntero o una referencia.
C++ Java
clase Foo {} // Declares clase Foo int x = 0; // Variable de Miembro Privado. Lo hará. // ser inicializado a 0, si el // constructor no lo establecería. // (de C+11) público: Foo() : x()0) // Constructor for Foo; inicializa {} // x a 0. Si el inicializador fuera // omitido, la variable // ser inicializado al valor que // se ha dado en declaración de x. int bar()int i) {} // Función miembro bar() retorno 3*i + x; }};
clase Foo {} // Define la clase Foo privado int x; // Variable miembro, normalmente declarada // como privado para hacer cumplir la encapsulación // inicializado a 0 por defecto público Foo() {} // Constructor para Foo } // no-arg constructor suministrado por defecto público int bar()int i) {} // Miembro método bar() retorno 3*i + x; }}
Foo a;// declara ser un valor de objeto Foo,// inicializado utilizando el constructor predeterminado.// Otro constructor se puede utilizar comoFoo a()args);// o (C+11):Foo a{}args};
Foo a = nuevo Foo();// declara ser una referencia a un nuevo objeto Foo// inicializado utilizando el constructor predeterminado// Otro constructor se puede utilizar comoFoo a = nuevo Foo()args);
Foo b = a;// copia el contenido de a a un nuevo objeto Foo b;// Sintaxis alternativa es "Foo b(a)"
// Foo b = a;// declararía b ser referencia al objeto señalado por unFoo b = a.clone();// copia el contenido del objeto apuntado por un // a un nuevo objeto Foo;// establece la referencia b para apuntar a este nuevo objeto;// la clase Foo debe implementar la interfaz de Cloneable// para este código para compilar
a.x = 5; // modifica el objeto a
a.x = 5; // modifica el objeto referenciado por un
std::Cout .. b.x .. std::endl;// salidas 0, porque b es// algún objeto distinto a un
Sistema.Fuera..println()b.x);// salidas 0, porque b puntos a// algún objeto distinto a un
Foo *c;// declara c ser un puntero a un// Objeto Foo (inicialmente// indefinido; podría apuntar a cualquier lugar)
Foo c;// declara c ser una referencia a un Foo// objeto (inicialmente nulo si c es miembro de clase;// es necesario inicializar c antes de usar// si es una variable local)
c = nuevo Foo;// c se establece en el valor de la dirección del objeto Foo creado por el operador nuevo
c = nuevo Foo();// binds c para hacer referencia a un nuevo objeto Foo
Foo "d = *c;// binds d para hacer referencia al mismo objeto al que c puntos
Foo d = c;// binds d para hacer referencia al mismo objeto que c
c-x = 5;// modifica el objeto apuntado por c
c.x = 5;// modifica el objeto referenciado por c
d.bar()5); // invoca Foo::bar() para ac-bar()5); // invoca Foo::bar() for *c
d.bar()5); // invoca Foo.bar() para ac.bar()5); // invoca Foo.bar() para c
std::Cout .. d.x .. std::endl;// salidas 5, porque d referencias el// mismo objeto al que c puntos
Sistema.Fuera..println()d.x);// salidas 5, porque d referencias el// mismo objeto que c
  • En C++, es posible declarar un puntero o referencia a un objeto const para evitar que el código del cliente lo modifique. Funciones y métodos también pueden garantizar que no modificarán el objeto apuntado por un puntero utilizando la palabra clave "const". Esto impone la correlación.
  • En Java, en su mayor parte, la corrección const debe depender de la semántica de la interfaz de la clase, es decir, no se aplica fuertemente, excepto los miembros de datos públicos que son etiquetados final.
C++ Java
const Foo *a; // no es posible modificar el objeto // apuntado por un a través de
final Foo a; // una declaración de una referencia "final": // es posible modificar el objeto,  // pero la referencia apuntará constantemente  // al primer objeto asignado a él
a = nuevo Foo();
a = nuevo Foo(); // Sólo en constructor
a-x = 5;// ILLEGAL
a.x = 5;// LEGAL, los miembros del objeto todavía pueden ser modificados // a menos que se declare explícitamente final en la clase declarante
Foo *const b = nuevo Foo();// una declaración de un puntero "const"// es posible modificar el objeto,// pero el puntero apuntará constantemente// al objeto asignado a él aquí
final Foo b = nuevo Foo();// una declaración de una referencia "final"
b = nuevo Foo();// ILLEGAL, no se permite volver a unirlo
b = nuevo Foo();// ILLEGAL, no se permite volver a unirlo
b-x = 5;// LEGAL, el objeto todavía puede ser modificado
b.x = 5;// LEGAL, el objeto todavía puede ser modificado
  • Soportes C++ goto declaraciones, que pueden llevar a la programación de códigos espagueti. Con la excepción de la declaración de Goto (que es muy raramente vista en código real y altamente desalentado), tanto Java como C++ tienen básicamente las mismas estructuras de flujo de control, diseñadas para hacer cumplir el flujo de control estructurado, y depende de la ruptura y continuar las declaraciones para proporcionar algunos goto- como funciones. Algunos comentaristas señalan que estas declaraciones de control de flujo etiquetadas rompen la propiedad de punto de salida único de programación estructurada.
  • C++ ofrece características de bajo nivel que Java en su mayoría carece (una excepción notable es la sun.misc.Unsafe API para acceso directo a la memoria y manipulación). En C++, los punteros se pueden utilizar para manipular lugares específicos de memoria, una tarea necesaria para escribir componentes de sistemas operativos de bajo nivel. Del mismo modo, muchos compiladores C+ apoyan a un montador en línea. El código de idioma de la Asamblea se puede importar a un programa C y viceversa. Esto hace que el lenguaje C sea aún más rápido. En Java, dicho código debe residir en bibliotecas externas, y sólo se puede acceder a través de la Interfaz Nativa Java, con una sobrecarga significativa para cada llamada.

Semántica

  • C++ permite valores predeterminados para argumentos de una función/método. Java no. Sin embargo, la sobrecarga de método se puede utilizar para obtener resultados similares en Java pero generar código de obstrucción redundante.
  • El mínimo de código necesario para compilar para C++ es una función, ya que Java es una clase.
  • C++ permite una gama de conversiones implícitas entre tipos nativos (incluyendo algunas conversiones de estrechamiento), y también permite definir conversiones implícitas con tipos definidos por el usuario. En Java, sólo las conversiones crecientes entre los tipos nativos son implícitas; otras conversiones requieren una sintaxis explícita de fundición.
    • A result of this is that although loop conditions (if, while y la condición de salida en for) en Java y C++ ambos esperan una expresión booleana, código como if(a = 5) causará un error de compilación en Java porque no hay una conversión de estrechamiento implícita de int a booleano, pero se compilará en C++. Esto es útil si el código era un typo y if(a == 5) estaba destinado. Sin embargo, los compiladores actuales C++ generalmente generarán una advertencia cuando dicha asignación se realiza dentro de una expresión condicional. Del mismo modo, declaraciones de comparación independientes, por ejemplo. a==5;, sin un efecto secundario generalmente conduce a una advertencia.
  • Para pasar los parámetros a las funciones, C++ soporta tanto el paso a la referencia como el valor paso a paso. En Java, los parámetros primitivos siempre se transmiten por valor. Tipos de clase, tipos de interfaz y tipos de array son llamados colectivamente tipos de referencia en Java y también siempre se pasan por valor.
  • Los tipos incorporados de Java son de un tamaño y rango definidos por la especificación del lenguaje. En C++, se define una gama mínima de valores para los tipos incorporados, pero la representación exacta (número de bits) se puede mapear a cualquier tipo nativo que se prefiera en una plataforma dada.
    • Por ejemplo, caracteres Java son caracteres Unicode de 16 bits, y cadenas están compuestas de una secuencia de tales caracteres. C++ ofrece caracteres angostos y anchos, pero el tamaño real de cada una es dependiente de la plataforma, al igual que el conjunto de caracteres utilizado. Las cuerdas se pueden formar de cualquier tipo.
    • Esto también implica que los compiladores C+ pueden seleccionar automáticamente la representación más eficiente para la plataforma de destino (es decir, números de 64 bits para una plataforma de 64 bits), mientras que la representación está fijada en Java, lo que significa que los valores pueden ser almacenados en el tamaño menos eficiente, o debe remar los bits restantes y añadir código para emular el comportamiento de menor ancho.
  • La redondeo y precisión de los valores y operaciones de puntos flotantes en C++ es definida por la implementación (aunque sólo plataformas muy exóticas o antiguas parten del estándar IEEE 754). Java proporciona una opción modelo estricto de punto flotante (strictfp) que garantiza resultados más consistentes a través de plataformas, aunque al costo de un rendimiento de tiempo de ejecución posiblemente más lento. Sin embargo, Java no cumple estrictamente con el estándar IEEE 754. La mayoría de los compiladores C++, por defecto, cumplirán parte con IEEE 754 (usualmente excluyendo reglas estrictas de redondeo y plantear excepciones en los resultados de NaN), pero proporcionarán opciones de cumplimiento de diversa rigor, para permitir algunas optimización. Si etiquetamos esas opciones desde menos compatibles con la mayoría de la rápido, coherente (Java's strictfp), cerca de IIEEE, y estricto-IEEE, podemos decir que la mayoría de las implementaciones C++ predeterminados a cerca de IIEEE, con opciones para cambiar a rápido o estricto-IEEE, mientras Java predetermina rápido con una opción para cambiar coherente.
  • En C++, los punteros pueden ser manipulados directamente como valores de dirección de memoria. Las referencias de Java son apuntadores a objetos. Las referencias de Java no permiten el acceso directo a las direcciones de memoria o permiten manipular las direcciones de memoria con puntero aritmético. En C++ se puede construir punteros a punteros, punteros a puntos y dobles, y punteros a lugares de memoria arbitrarios. Las referencias de Java sólo acceden a objetos, nunca primitivos, otras referencias, o sitios de memoria arbitrarios. En Java, la memoria puede ser leída y escrita por valores arbitrarios utilizando sun.misc.Unsafe API, sin embargo, es deprecatado y no recomendado.
  • En C++, los punteros pueden apuntar a funciones o funciones miembros (puntores de funciones). El mecanismo equivalente en Java utiliza referencias de objeto o interfaz.
  • A través de objetos apilados, C++ es compatible con la gestión de recursos de alcance, una técnica utilizada para gestionar automáticamente la memoria y otros recursos del sistema que soportan la destrucción de objetos deterministas. Aunque la gestión de los recursos abarcados en C++ no puede garantizarse (incluso los objetos con destructores adecuados pueden asignarse utilizando new e izquierda no eliminada) proporciona un medio eficaz de gestión de recursos. Los recursos compartidos se pueden gestionar utilizando shared_ptr, junto con weak_ptr para romper referencias cíclicas. Java apoya la gestión automática de la memoria mediante la recolección de basura que puede liberar objetos inalcanzables incluso en presencia de referencias cíclicas, pero otros recursos del sistema (archivos, arroyos, ventanas, puertos de comunicación, hilos, etc.) deben ser liberados explícitamente porque la colección de basura no está garantizada a ocurrir inmediatamente después de que la última referencia del objeto sea abandonada.
  • C++ cuenta con una sobrecarga de operador definida por el usuario. La sobrecarga de operador permite que los tipos definidos por el usuario apoyen a los operadores (aritmética, comparaciones, etc.) como tipos primitivos mediante implementaciones definidas por el usuario para estos operadores. Generalmente se recomienda preservar la semántica de los operadores. Java no soporta ninguna forma de sobrecarga del operador (aunque su biblioteca utiliza el operador de adición para la concatenación de cadenas).
  • Java cuenta con interfaz de programación de aplicaciones estándar (API) para la reflexión y carga dinámica de nuevo código arbitrario.
  • C++ soporta la vinculación estática y dinámica de los binarios.
  • Java tiene genéricos, cuyo objetivo principal es proporcionar contenedores seguros de tipo. C++ tiene plantillas compiladas, que proporcionan un apoyo más amplio para la programación genérica y la metaprogramación. Java tiene anotaciones, que permiten añadir metadatos personalizados arbitrarios a clases y metaprogramar a través de una herramienta de procesamiento de anotaciones.
  • Tanto Java como C+ distinguen entre tipos nativos (también denominados fundamentales o incorporado tipos) y tipos definidos por el usuario (también denominados compuesto tipos). En Java, los tipos nativos tienen valor semántico solamente, y los tipos compuestos tienen referencia sólo semántica. En C++ todos los tipos tienen semántica de valor, pero se puede crear una referencia a cualquier tipo, lo que permitirá que el objeto sea manipulado mediante semántica de referencia.
  • C++ admite múltiples herencias de clases arbitrarias. En Java una clase puede derivarse de una sola clase, pero una clase puede implementar múltiples interfaces (en otras palabras, soporta múltiples herencias de tipos, pero sólo una sola herencia de implementación).
  • Java distingue explícitamente entre interfaces y clases. En C+++, múltiples herencias y funciones virtuales puras permiten definir clases que funcionan casi como las interfaces Java, con algunas pequeñas diferencias.
  • Java tiene soporte de idiomas y biblioteca estándar para multi-threading. El synchronized palabra clave en Java proporciona cerraduras de mutex simples y seguras para soportar aplicaciones multi-treaded. Java también ofrece bibliotecas robustas y complejas para la sincronización de múltiples hilos más avanzada. Sólo a partir de C+11 hay un modelo de memoria definido para la multi-telección en C++, y soporte bibliotecario para crear hilos y para muchos sincronizar primitivos. También hay muchas bibliotecas de terceros para esto.
  • Las funciones de miembro C++ pueden ser declaradas como funciones virtuales, lo que significa que el método a llamar se determina por el tipo de tiempo de ejecución del objeto (a.k.a. envío dinámico). Por defecto, los métodos en C+ no son virtuales (es decir, opt-in virtual). En Java, los métodos son virtuales por defecto, pero se pueden hacer no virtualmente utilizando el final palabra clave (es decir, opt-out virtual).
  • Las enumeraciones C++ son tipos primitivos y soportan la conversión implícita a tipos enteros (pero no de tipos enteros). Las enumeraciones de Java pueden ser public static enum{enumName1,enumName2} y se usan como clases. Otra manera es hacer otra clase que se extiende java.lang.Enum) y por lo tanto puede definir constructores, campos y métodos como cualquier otra clase. A partir de C++11, C++ también admite enumeraciones de tipo fuerte que proporcionan más seguridad de tipo y especificación explícita del tipo de almacenamiento.
  • Operadores no deseados '++' y '--': en C+++ "El operario será un valor modificado. [Funcionado] El resultado es el operado actualizado; es un valor l...", pero en Java "la promoción numérica binaria mencionada anteriormente puede incluir la conversión de unboxing y la conversión del valor establecido. Si es necesario, la conversión de conjunto de valor {y/o [...] conversión de boxeo} se aplica a la suma antes de que se almacene en la variable.", es decir, en Java, después de la inicialización "Integer i=2;", "++i;" cambia la referencia i asignando nuevo objeto, mientras que en C++ el objeto sigue siendo el mismo.

Gestión de recursos

  • Java ofrece una colección automática de basura, que puede ser superada en circunstancias específicas a través de la especificación Java en tiempo real. La gestión de memoria en C+ se hace generalmente a través de constructores, destructores y punteros inteligentes. El estándar C++ permite la recogida de basura, pero no lo requiere. La colección de basura rara vez se utiliza en la práctica.
  • C++ puede asignar bloques arbitrarios de memoria. Java sólo asigna la memoria a través de la instantánea del objeto. Los bloques de memoria arbitrarios se pueden asignar en Java como una serie de bytes.
  • Java y C++ utilizan diferentes expresiones para la gestión de recursos. Java se basa principalmente en la recogida de basura, que puede recuperar la memoria, mientras que C++ se basa principalmente en la idioma de adquisición de recursos. Esto se refleja en varias diferencias entre los dos idiomas:
    • En C++ es común asignar objetos de tipos compuestos como variables locales de pila que se destruyen cuando salen del alcance. En los tipos compuestos de Java siempre se asignan en el montón y recogidos por el colector de basura (excepto en máquinas virtuales que utilizan el análisis de escape para convertir asignaciones de montones a las asignaciones de pila).
    • C++ tiene destructores, mientras que Java tiene finalizadores. Ambos son invocados antes de la distribución de un objeto, pero difieren significativamente. El destructor de un objeto C++ debe ser invocado implícitamente (en el caso de las variables con pila) o explícitamente para tratar un objeto. El destructor ejecuta sincrónicamente justo antes del punto en un programa en el que un objeto es deallocado. Sincrónico, coordinado uninicialización y repartición en C++ satisfacen así el idioma RAII. En Java, la distribución de objetos es manejada implícitamente por el recolector de basura. El finalizador de un objeto Java es invocado asincrónicamente algún tiempo después de que haya sido accedido por última vez y antes de que sea desalentado. Muy pocos objetos necesitan finalizadores. Un finalizador es necesario por sólo objetos que deben garantizar una cierta limpieza del estado objeto antes de la adjudicación, normalmente liberando recursos externos al JVM. Además, los finalizadores vienen con severas penalizaciones de rendimiento y aumentan significativamente el tiempo necesario para que los objetos sean desanimados, por lo que su uso es desalentado y deprecatado en Java 9.
    • Con RAII en C++, un tipo de recurso se envuelve normalmente dentro de una clase pequeña que asigna el recurso a la construcción y libera el recurso sobre la destrucción, y proporciona acceso al recurso entre esos puntos. Cualquier clase que contenga sólo tales objetos RAII no necesita definir un destructor ya que los destructores de los objetos RAII se llaman automáticamente como un objeto de esta clase es destruido. En Java, la distribución segura sincrónica de los recursos se puede realizar determinísticamente utilizando el intento/catch/finally construct.
    • En C++, es posible tener un puntero brillante, una referencia fija a un objeto que ya ha sido desalentado. Tratar de usar un puntero brillante generalmente resulta en la falla del programa. En Java, el recolector de basura no destruirá un objeto referenciado.
    • En C++, es posible tener objetos primitivos ininicializados. Java impone la inicialización predeterminada.
    • En C++, es posible tener un objeto asignado al que no hay referencia válida. Tal objeto inalcanzable no puede ser destruido (deallocado), y resulta en una fuga de memoria. En contraste, en Java un objeto no será desalentado por el recolector de basura hasta se vuelve inalcanzable (por el programa del usuario). ()Referencias débiles son compatibles, que trabajan con el colector de basura Java para permitir diferentes fortalezas de la accesibilidad.) La colección de basura en Java evita muchas fugas de memoria, pero las fugas siguen siendo posibles en algunas circunstancias.

Bibliotecas

  • C++ ofrece acceso multiplataforma a muchas características típicamente disponibles en bibliotecas específicas de plataforma. El acceso directo desde Java al sistema operativo nativo y las funciones de hardware requiere el uso de la interfaz nativa de Java.

Tiempo de ejecución

C++ Java
C++ se compila directamente al código de máquina que luego es ejecutado directamente por la unidad central de procesamiento. Java está compilado a byte-code que la máquina virtual Java (JVM) interpreta a tiempo de ejecución. Las implementaciones actuales de Java se recopilan a tiempo justo a código de máquina nativo. Alternativamente, el Compiler GNU para Java puede compilar directamente al código de máquina.
  • Debido a su expresividad no constreñida, las características de lenguaje C++ de bajo nivel (por ejemplo, acceso sin control de matriz, punteros crudos, tipo punning) no pueden ser revisados fiablemente a tiempo de compilación o sin sobrecabeza en tiempo de ejecución. Los errores relacionados de programación pueden conducir a flujos de amortiguación de bajo nivel y fallas de segmentación. La Biblioteca de Plantillas Estándar ofrece abstracciones RAII de alto nivel (como vector, lista y mapa) para ayudar a evitar tales errores. En Java, los errores de bajo nivel no pueden ocurrir o son detectados por la máquina virtual Java (JVM) y reportados a la aplicación en forma de excepción.
  • El lenguaje Java requiere comportamiento específico en el caso de un acceso de matriz fuera de límites, que generalmente requiere la comprobación de límites de accesos de matriz. Esto elimina una posible fuente de inestabilidad, pero generalmente a costa de ralentizar la ejecución. En algunos casos, especialmente desde Java 7, el análisis del compilador puede demostrar que los límites no son necesarios y eliminarlo. C++ no tiene ningún comportamiento necesario para el acceso fuera de límites de los arrays nativos, por lo que no requiere ningún límite para comprobar los arrays nativos. C++ colecciones estándar de bibliotecas como std::vector, sin embargo, ofrecen la comprobación de límites opcionales. En resumen, los arrays Java son "usualmente seguros; ligeramente limitados; a menudo tienen overhead" mientras que los arrays nativos C++ "tienen sobrecabezamiento opcional; son ligeramente inconstricados; son posiblemente inseguros".

Plantillas frente a genéricos

Tanto C++ como Java brindan funciones para programación genérica, plantillas y genéricos, respectivamente. Aunque fueron creados para resolver tipos de problemas similares y tienen una sintaxis similar, son bastante diferentes.

Plantillas C++ Java Generics
Clases, funciones, alias y variables pueden ser plantilladas. Las clases y métodos pueden ser genéricos.
Los parámetros pueden ser varídicos, de cualquier tipo, valor integral, literal de carácter o plantilla de clase. Los parámetros pueden ser cualquier tipo de referencia, incluyendo los tipos primitivos boxeados (es decir, Integer, Boolean...).
Se generarán instantáneas separadas de la clase o función para cada parámetro-set cuando se compila. Para plantillas de clase, sólo las funciones de miembro que se utilizan serán instantáneas. Una versión de la clase o función se compila, funciona para todos los parámetros de tipo (a través de la evaluación de tipo).
Los objetos de una plantilla de clase instantáneas con diferentes parámetros tendrán diferentes tipos en el tiempo de ejecución (es decir, distintas instantáneas de plantilla son clases distintas). Los parámetros de tipo se borran cuando se compilan; los objetos de una clase con diferentes parámetros de tipo son el mismo tipo en el tiempo de ejecución. Causa un constructor diferente. Debido a este tipo de borrado, no es posible sobrecargar métodos utilizando diferentes instantáneas de la clase genérica.
La implementación de la plantilla de clase o función debe ser visible dentro de una unidad de traducción para utilizarla. Esto generalmente implica tener las definiciones en los archivos de encabezado o incluido en el archivo de encabezado. A partir de C++11, es posible utilizar plantillas externas para separar la compilación de algunas instantáneas. La firma de la clase o función de un archivo de clase compilado es suficiente para usarlo.
Las plantillas pueden ser especializadas: se puede proporcionar una aplicación separada para un parámetro de plantilla particular. La genérica no puede ser especializada.
Los parámetros de plantilla pueden tener argumentos predeterminados. Pre-C++11, esto se permitió sólo para las clases de plantilla, no funciones. Los parámetros genéricos del tipo no pueden tener argumentos predeterminados.
Salvajes sin soporte. En su lugar, los tipos de retorno están disponibles a menudo como tipos anidados. (también, C+11 añadido palabra clave auto, que actúa como un comodín para cualquier tipo que se pueda determinar en el tiempo de compilación.) Wildcards soportados como parámetro tipo.
No hay soporte directo para la fijación de parámetros de tipo, pero la metaprogramación proporciona esto Soporta la fijación de parámetros de tipo con "extends" y "super" para los límites superiores e inferiores, respectivamente; permite la ejecución de relaciones entre los parámetros de tipo.
Permite la instantánea de un objeto con el tipo del parámetro. Precludes instantiation of an object with the type of the parameter type (except via reflection).
Tipo parámetro de plantilla de clase se puede utilizar para métodos estáticos y variables. El parámetro tipo de clase genérica no se puede utilizar para métodos estáticos y variables.
Variables estaticas no divididas entre clases y funciones de diferentes parámetros de tipo. Variables estaticas compartidas entre instancias de clases de diferentes parámetros de tipo.
Las plantillas de clase y función no imponen relaciones de tipo para parámetros de tipo en su declaración. El uso de un parámetro de tipo incorrecto resulta en el fallo de compilación, a menudo generando un mensaje de error dentro del código de plantilla en lugar de en el código del usuario que lo invoca. El uso adecuado de las clases y funciones de plantilla depende de la documentación adecuada. Metaprogramación proporciona estas características al costo del esfuerzo añadido. Hubo una propuesta para resolver este problema en C++11, así llamados Conceptos, se planea para el siguiente estándar. Clases y funciones genéricas pueden hacer cumplir relaciones de tipo para parámetros de tipo en su declaración. El uso de un parámetro de tipo incorrecto resulta en un error de tipo dentro del código que lo utiliza. Las operaciones sobre los tipos parametrizados en código genérico sólo se permiten en formas que pueden garantizarse a ser seguras por la declaración. Esto resulta en una mayor seguridad de tipo al costo de la flexibilidad.
Las plantillas son Turing-complete (ver metaprogramación de plantilla). Genéricos son también Turing-complete

Varios

  • Java y C++ utilizan diferentes medios para dividir el código en múltiples archivos fuente. Java utiliza un sistema de paquetes que dicta el nombre de archivo y la ruta para todas las definiciones del programa. Su compilador importa los archivos de clase ejecutables. C++ utiliza un sistema de inclusión de código fuente de archivo de encabezado para compartir declaraciones entre archivos fuente.
  • Los archivos de código Java compilados son generalmente más pequeños que los archivos de código en C++ ya que Java bytecode es generalmente más compacto que el código de máquina nativo y los programas Java nunca están conectados estadísticamente.
  • La compilación C++ cuenta con una fase de preprocesamiento textual agregada, mientras que Java no. Así, algunos usuarios añaden una fase de preprocesamiento a su proceso de construcción para un mejor apoyo a la compilación condicional.
  • Los operadores de división y módulo de Java están bien definidos para truncar a cero. C++ (pre-C++11) no especifica si estos operadores se ajustan o no a cero o "confian a -infinidad". -3/2 siempre será -1 en Java y C++11, pero un compilador C++03 puede volver ya sea -1 o -2, dependiendo de la plataforma. C99 define división de la misma manera que Java y C++11. Ambos idiomas garantizan (donde a y b son tipos enteros) que (a/b)*b + (a%b) == a para todos a y b (b!= 0). La versión C++03 a veces será más rápida, ya que se permite elegir cualquier modo de truncación es nativo del procesador.
  • Los tamaños de los tipos enteros se definen en Java (int is 32-bit, long is 64-bit), mientras que en C++ el tamaño de enteros y punteros es compilador y aplicación de interfaz binaria (ABI) dependiente dentro de limitaciones dadas. Así, un programa Java tendrá un comportamiento consistente a través de plataformas, mientras que un programa C++ puede requerir adaptación para algunas plataformas, pero puede funcionar más rápido con tamaños de entero más naturales para la plataforma local.

Existe un ejemplo que compara C++ y Java en Wikilibros.

Rendimiento

Además de ejecutar un programa Java compilado, las computadoras que ejecutan aplicaciones Java generalmente también deben ejecutar la máquina virtual Java (JVM), mientras que los programas C++ compilados pueden ejecutarse sin aplicaciones externas. Las primeras versiones de Java fueron superadas significativamente por lenguajes compilados estáticamente como C++. Esto se debe a que las declaraciones de programa de estos dos lenguajes estrechamente relacionados pueden compilarse en unas pocas instrucciones de máquina con C++, mientras que se compilan en varios códigos de bytes que involucran varias instrucciones de máquina, cada uno cuando es interpretado por una JVM. Por ejemplo:

Declaración Java/C++ C+++ Código generado (x86) Java generado código byte
vector[i]++;
mov edx,[ebp+4h]mov eax,[ebp+1Ch]inc dword ptr [edx+eax*4]
aload_1
iload_2
dup2
iaload
iconst_1
iadd
iastore

Dado que la optimización del rendimiento es un tema muy complejo, es muy difícil cuantificar la diferencia de rendimiento entre C++ y Java en términos generales, y la mayoría de los puntos de referencia no son confiables y están sesgados. Dada la naturaleza muy diferente de los idiomas, también es difícil establecer diferencias cualitativas definitivas. En pocas palabras, existen ineficiencias inherentes y límites estrictos en la optimización en Java, dado que depende en gran medida de abstracciones flexibles de alto nivel; sin embargo, el uso de un potente compilador JIT (como en las implementaciones modernas de JVM) puede mitigar algunos problemas. En cualquier caso, si las ineficiencias de Java son demasiado grandes, el código C o C++ compilado se puede llamar desde Java a través de JNI.

Algunas ineficiencias que son inherentes al lenguaje Java incluyen, principalmente:

  • Todos los objetos se asignan en el montón. Aunque la asignación es extremadamente rápida en los JVM modernos utilizando 'bump allocation', que realiza de forma similar a la asignación de pila, el rendimiento todavía puede ser impactado negativamente debido a la invocación del recolector de basura. Los compiladores JIT modernos mitiguen este problema en cierta medida con el análisis de escape o la detección de escape para asignar algunos objetos en la pila, desde Oracle JDK 6.
  • Proyectos críticos de rendimiento como sistemas de bases de datos eficientes y bibliotecas de mensajería han tenido que utilizar API no oficiales internas como sun.misc.Unsafe obtener acceso a la gestión manual de recursos y ser capaz de apilar la asignación; manipular eficazmente los pseudopuntos.
  • Un montón de fundición de tiempo de ejecución requerido incluso utilizando contenedores estándar induce una penalización de rendimiento. Sin embargo, la mayoría de estos moldes son eliminados estadísticamente por el compilador JIT.
  • Las garantías de seguridad tienen un costo de funcionamiento. Por ejemplo, se requiere que el compilador ponga comprobaciones de rango apropiadas en el código. Guardar el acceso de cada matriz con un control de rango no es eficiente, por lo que la mayoría de los compiladores JIT tratarán de eliminarlos estaticamente o moviendolos fuera de los bucles interiores (aunque la mayoría de los compiladores nativos para C++ harán lo mismo cuando los controles de rango se utilizan opcionalmente).
  • La falta de acceso a detalles de bajo nivel impide al desarrollador mejorar el programa donde el compilador no puede hacerlo.
  • El uso obligatorio de la semántica de referencia para todos los tipos definidos por el usuario en Java puede introducir grandes cantidades de indirectas de memoria superfluas (o saltos) (a menos que el compilador JIT) que pueden conducir a frecuentes faltas de caché (a.k.a. cache thrashing). Además, la optimización de caché, generalmente a través de estructuras y algoritmos de datos cache-aware o cache-oblivious, puede llevar a menudo a órdenes de mejoras de magnitud en el rendimiento, así como evitar la degeneración de la complejidad del tiempo que es característica de muchos algoritmos de cache-pesimización, y por lo tanto es una de las formas más importantes de optimización; referencia-semántica, como mandato en Java, hace que tales optimizaciones imposibles para realizar el programar
  • Coleccion de basura, ya que esta forma de gestión automática de memoria introduce la sobrecarga de memoria.

Sin embargo, hay una serie de beneficios en el diseño de Java, algunos realizados, otros solo teóricos:

  • colección de basura Java puede tener mejor coherencia de caché que el uso habitual de malloc/nuevo para la asignación de memoria. Sin embargo, existen argumentos de que ambos aficionados fragmentan igualmente el montón y tampoco exhiben una mejor localización de caché. Sin embargo, en C++, la asignación de objetos individuales en el montón es rara, y grandes cantidades de objetos individuales se asignan generalmente en bloques a través de un contenedor STL y/o con un pequeño conjuntor de objetos.
  • Compilación de tiempo de ejecución puede potencialmente utilizar información sobre la plataforma en la que se ejecuta el código para mejorar el código más eficazmente. Sin embargo, la mayoría de los compiladores nativos de última generación (C, C++, etc.) generan múltiples caminos de código para emplear las habilidades computacionales completas del sistema dado. También se puede hacer el argumento inverso de que los compiladores nativos pueden aprovechar mejor los conjuntos de optimización e instrucción propios de la arquitectura que las distribuciones JVM multiplataforma.
  • Compilación de tiempo de ejecución permite una función virtual más agresiva que es posible para un compilador estático, ya que el compilador JIT tiene más información sobre todos los objetivos posibles de llamadas virtuales, incluso si están en diferentes módulos cargados dinámicamente. Actualmente disponibles las implementaciones de JVM no tienen ningún problema en inlinear la mayoría de las llamadas monomorfónicas, en su mayoría monomórficas y dimorfónicas, y la investigación está en marcha para inlinear también llamadas megamorfónicas, gracias a las recientes mejoras dinámicas invocadas agregadas en Java 7. La inclusión puede permitir nuevas optimizaciones como la vectorización de bucles o la desrollación de bucles, lo que resulta en un enorme aumento general de rendimiento.
  • En Java, la sincronización de hilos se construye en el lenguaje, por lo que el compilador JIT puede potencialmente, a través del análisis de escape, cerraduras de elide, mejorar significativamente el rendimiento de código ingenuo de varios hilos.

Además, se producen algunos problemas de rendimiento en C++:

  • Permitir que los punteros señalen cualquier dirección puede dificultar la optimización debido a la posibilidad de aliarse puntero.
  • Dado que el código generado a partir de varias instantáneas de la misma plantilla de clase en C++ no es compartido (como con genéricos de tipo en Java), el uso excesivo de plantillas puede llevar a un aumento significativo del tamaño de código ejecutable (color de código). Sin embargo, debido a que las plantillas de función son agresivamente inlineadas, a veces pueden reducir el tamaño del código, pero lo más importante es permitir un análisis estático más agresivo y la optimización del código por parte del compilador, más a menudo haciéndolo más eficiente que el código no tentado. En cambio, los genéricos de Java son necesariamente menos eficientes que el código no genérico.
  • Debido a que en un compilador tradicional C++, el enlace dinámico se realiza después de la generación y optimización de códigos en C++, las llamadas de función que abarcan diferentes módulos dinámicos no pueden ser inlineadas. Sin embargo, los compiladores modernos C++ como MSVC y Clang+LLVM ofrecen opciones de generación de códigos de conexión que permiten la compilación de módulos a formatos intermedios que permiten inlinear en la etapa final del enlace.

Estándar oficial y referencia del idioma

Especificación de idioma

El lenguaje C++ está definido por ISO/IEC 14882, un estándar ISO, publicado por el comité ISO/IEC JTC1/SC22/WG21. El último borrador posterior a la estandarización de C++17 también está disponible.

El lenguaje C++ evoluciona a través de un comité directivo abierto denominado Comité de estándares de C++. El comité está compuesto por el creador de C++ Bjarne Stroustrup, el convocante Herb Sutter y otras figuras prominentes, incluidos muchos representantes de industrias y grupos de usuarios (es decir, las partes interesadas). Al ser un comité abierto, cualquiera es libre de unirse, participar y aportar propuestas para las próximas versiones de la norma y las especificaciones técnicas. El comité ahora tiene como objetivo publicar un nuevo estándar cada pocos años, aunque en el pasado los estrictos procesos de revisión y las discusiones han significado demoras más largas entre la publicación de nuevos estándares (1998, 2003 y 2011).

El lenguaje Java está definido por la Especificación del lenguaje Java, un libro publicado por Oracle.

El lenguaje Java evoluciona continuamente a través de un proceso llamado Java Community Process, y la comunidad mundial de programación está representada por un grupo de personas y organizaciones, los miembros de la comunidad Java, que participan activamente en la mejora del lenguaje, mediante el envío de solicitudes públicas, las solicitudes de especificación de Java, que deben pasar revisiones formales y públicas antes de que se integren en el lenguaje.

La falta de un estándar firme para Java y la naturaleza algo más volátil de sus especificaciones han sido una fuente constante de críticas por parte de las partes interesadas que desean más estabilidad y conservadurismo en la adición de nuevas funciones de lenguaje y biblioteca. En contraste, el comité de C++ también recibe críticas constantes, por la razón opuesta, es decir, es demasiado estricto y conservador, y tarda demasiado en lanzar nuevas versiones.

Marcas

"C++" no es una marca comercial de ninguna empresa u organización y no es propiedad de ningún individuo. "Java" es una marca comercial de Oracle Corporation.

Contenido relacionado

Quine (informática)

Estación terrestre de satélite Goonhilly

Amigo 1000

Más resultados...
Tamaño del texto:
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save