Comparação de Java e C ++ - Comparison of Java and C++

Esta é uma comparação de Java e C ++ , duas linguagens de programação orientadas a objetos proeminentes .

Objetivos de design

As diferenças entre as linguagens de programação C ++ e Java podem ser rastreadas em sua herança , pois têm objetivos de design diferentes.

C ++ foi projetado para a programação de sistemas e aplicativos ( ou seja, programação de infraestrutura), estendendo a linguagem de programação procedural C , que foi projetada para uma execução eficiente. Para C, C ++ adicionou suporte para programação orientada a objetos , tratamento de exceções , gerenciamento de recursos baseados na vida ( RAII ), programação genérica , metaprogramação de modelo e a C ++ Standard Library que inclui contêineres e algoritmos genéricos (a Biblioteca de Modelos Padrão ou STL), e muitas outras instalações de uso geral.

Java é uma linguagem de programação de uso geral, simultânea, baseada em classes e orientada a objetos, projetada para minimizar as dependências de implementação. Ele depende de uma máquina virtual Java para ser seguro e altamente portátil . Ele é empacotado com uma extensa biblioteca projetada para fornecer uma abstração completa da plataforma subjacente. Java é uma linguagem orientada a objetos estaticamente tipada que usa uma sintaxe semelhante (mas incompatível com) C ++. Inclui um sistema de documentação chamado Javadoc .

Os diferentes objetivos no desenvolvimento de C ++ e Java resultaram em diferentes princípios e trade-offs de design entre as linguagens. As diferenças são as seguintes:

C ++ Java
Estende C com programação orientada a objetos e programação genérica . O código C pode ser usado da maneira mais adequada. Fortemente influenciado pela sintaxe C ++ / C.
Compatível com o código-fonte C , exceto em alguns casos extremos . Fornece o Java Native Interface e recentemente o Java Native Access como uma forma de chamar diretamente o código C / C ++.
Escreva uma vez, compile em qualquer lugar (WOCA). Escreva uma vez, execute em qualquer lugar / em qualquer lugar (WORA / WORE).
Permite programação procedural , programação funcional , programação orientada a objetos , programação genérica e metaprogramação modelo . Favorece uma mistura de paradigmas. Permite programação procedural , programação funcional (desde Java 8) e programação genérica (desde Java 5), ​​mas encoraja fortemente o paradigma de programação orientada a objetos . Inclui suporte para a criação de linguagens de script .
É executado como código de máquina executável nativo para o (s) conjunto (s) de instrução de destino . É executado em uma máquina virtual .
Fornece tipos de objeto e nomes de tipo. Permite a reflexão por meio de informações de tipo de tempo de execução (RTTI). É reflexivo , permitindo metaprogramação e geração de código dinâmico em tempo de execução.
Possui vários padrões de compatibilidade binários (comumente Microsoft (para compilador MSVC) e Itanium / GNU (para quase todos os outros compiladores)). Possui um padrão de compatibilidade binária, plataforma cruzada para SO e compilador.
Verificação de limites automatizada opcional (por exemplo, o at()método em vectore stringcontêineres). Todas as operações devem ser verificadas por limite por todas as distribuições compatíveis do Java. O HotSpot pode remover a verificação de limites.
Suporte aritmético não assinado nativo . Aritmética nativa sem sinal não suportada. O Java 8 muda algumas coisas, mas os aspectos não são claros.
Limites mínimos padronizados para todos os tipos numéricos, mas os tamanhos reais são definidos pela implementação. Tipos padronizados estão disponíveis por meio da biblioteca padrão <cstdint>. Limites e tamanhos padronizados de todos os tipos primitivos em todas as plataformas.
Ponteiros, referências e passagem por valor são suportados para todos os tipos (primitivos ou definidos pelo usuário). Todos os tipos (tipos primitivos e tipos de referência ) são sempre passados ​​por valor.
O gerenciamento de memória pode ser feito manualmente por meio de new / delete, automaticamente por escopo ou por ponteiros inteligentes. Suporta destruição determinística de objetos. ABI de coleta de lixo padronizada em C ++ 11, embora os compiladores não sejam necessários para implementar a coleta de lixo. Coleta de lixo automática . Oferece suporte a um método finalize () não determinístico, cujo uso não é recomendado.
O gerenciamento de recursos pode ser feito manualmente ou por gerenciamento automático de recursos vitalícios ( RAII ). O gerenciamento de recursos geralmente deve ser feito manualmente ou automaticamente por meio de finalizadores, embora isso geralmente seja desencorajado. Possui try-with-resources para gerenciamento automático de recursos baseado em escopo (versão 7 em diante).

Também pode ser feito usando a API interna, sun.misc.Unsafemas esse uso é altamente desencorajado e será substituído por uma API pública em uma próxima versão do Java.

Oferece suporte a classes, estruturas ( tipos de estrutura de dados passivos (PDS)) e uniões e pode alocá-los no heap ou na pilha . As classes são alocadas no heap . Java SE 6 otimiza com análise de escape para alocar alguns objetos na pilha .
Permite a substituição explícita de tipos e algumas conversões de estreitamento implícitas (para compatibilidade com C). Segurança de tipo rígido , exceto para conversões de alargamento.
A Biblioteca Padrão C ++ foi projetada para ter um escopo e funções limitados, mas inclui suporte de linguagem, diagnósticos, utilitários gerais, strings, localidades, contêineres, algoritmos, iteradores , numéricos, entrada / saída, geradores de números aleatórios, análise de expressão regular, recursos de threading , características de tipo (para introspecção de tipo estático) e Biblioteca C padrão. A biblioteca Boost oferece mais funções, incluindo E / S de rede.

Existe uma grande quantidade de bibliotecas de terceiros para GUI e outras funções como: Adaptive Communication Environment (ACE), Crypto ++ , várias bibliotecas XMPP Instant Messaging (IM), OpenLDAP , Qt , gtkmm .

A biblioteca padrão cresceu a cada lançamento. Na versão 1.6, a biblioteca incluía suporte para localidades, registro, contêineres e iteradores, algoritmos, programação GUI (mas não usando a GUI do sistema), gráficos, multi-threading, rede, segurança de plataforma, introspecção, carregamento dinâmico de classe, bloqueio e não -bloqueio de E / S. Ele forneceu interfaces ou classes de suporte para XML , XSLT , MIDI , conectividade de banco de dados, serviços de nomenclatura (por exemplo, LDAP ), criptografia, serviços de segurança (por exemplo, Kerberos ), serviços de impressão e serviços da web. O SWT ofereceu uma abstração para GUIs específicas da plataforma, mas foi substituído pelo JavaFX nas versões mais recentes; permitindo aceleração de gráficos e interfaces de usuário com temas CSS. Embora não suporte nenhum tipo de suporte de "aparência de plataforma nativa".
Sobrecarga do operador para a maioria dos operadores. Preservar o significado (semântica) é altamente recomendado. Os operadores não podem ser substituídos. A linguagem substitui + e + = para a classe String.
Herança única e múltipla de classes, incluindo herança virtual. Suporta apenas herança única de classes.
Modelos de tempo de compilação. Permite a meta-programação completa de Turing . Os genéricos são usados ​​para atingir a parametrização de tipo básica, mas não são convertidos do código-fonte para o código de bytes devido ao uso de eliminação de tipo pelo compilador.
Ponteiros de função, objetos de função, lambdas (em C ++ 11 ) e interfaces. Referências de funções, objetos de função e lambdas foram adicionados no Java 8 . As classes (e as interfaces, que são classes) também podem ser passadas como referências por meio de SomeClass.classe someObject.getClass().
Nenhum mecanismo de documentação embutido padrão. Existe software de terceiros (por exemplo, Doxygen ). Extenso padrão de documentação Javadoc em todas as classes e métodos do sistema.
constpalavra-chave para definir variáveis ​​imutáveis ​​e funções de membro que não alteram o objeto. A constância é propagada como um meio de impor, em tempo de compilação, a correção do código com relação à mutabilidade dos objetos (consulte correção da constância ). finalfornece uma versão de const, equivalente a type* constponteiros para objetos e constpara tipos primitivos. Imutabilidade de membros de objeto alcançada por meio de interfaces somente leitura e encapsulamento de objeto.
Apoia a gotodeclaração. Suporta rótulos com loops e blocos de instrução. gotoé uma palavra-chave reservada, mas está marcada como "não utilizada" na especificação Java.
O código-fonte pode ser escrito para ser multiplataforma (pode ser compilado para Windows , BSD , Linux , macOS , Solaris , etc., sem modificação) e escrito para usar recursos específicos da plataforma. Normalmente compilado em código de máquina nativo, deve ser recompilado para cada plataforma de destino. Compilado em bytecode Java para o JVM . O código de byte depende da plataforma Java, mas normalmente é independente dos recursos específicos do sistema operacional .

Características da linguagem

Sintaxe

  • A sintaxe Java possui uma gramática livre de contexto que pode ser analisada por um analisador LALR simples . Analisar C ++ é mais complicado. Por exemplo, Foo<1>(3);é uma sequência de comparações se Foo for uma variável, mas cria um objeto se Foo for o nome de um modelo de classe.
  • C ++ permite constantes, variáveis ​​e funções no nível do namespace. Em Java, tais entidades devem pertencer a algum tipo dado e, portanto, devem ser definidas dentro de uma definição de tipo, uma classe ou uma interface .
  • Em C ++, objetos são valores, enquanto em Java não são. C ++ usa semântica de valor por padrão, enquanto Java sempre usa semântica de referência . Para optar pela semântica de referência em C ++, um ponteiro ou uma referência pode ser usado.
C ++ Java
class Foo {          // Declares class Foo
    int x = 0;       //  Private Member variable. It will
                     // be initialized to 0, if the
                     // constructor would not set it.
                     // (from C++11)
    public:
      Foo() : x(0)     //  Constructor for Foo; initializes
      {}               //  x to 0. If the initializer were
                     //  omitted, the variable would
                     //  be initialized to the value that
                     // has been given at declaration of x.

      int bar(int i) { // Member function bar()
          return 3*i + x;
      }
};
class Foo {               // Defines class Foo
    private int x;        // Member variable, normally declared
                          // as private to enforce encapsulation
                          // initialized to 0 by default

    public Foo() {        // Constructor for Foo
    }                     // no-arg constructor supplied by default

    public int bar(int i) {        // Member method bar()
        return 3*i + x;
    }
}
Foo a;
// declares a to be a Foo object value,
// initialized using the default constructor.

// Another constructor can be used as
Foo a(args);
// or (C++11):
Foo a{args};
Foo a = new Foo();
// declares a to be a reference to a new Foo object
// initialized using the default constructor

// Another constructor can be used as
Foo a = new Foo(args);
Foo b = a;
// copies the contents of a to a new Foo object b;
// alternative syntax is "Foo b(a)"
// Foo b = a;
// would declare b to be reference to the object pointed to by a
Foo b = a.clone();
// copies the contents of the object pointed to by a 
//     to a new Foo object;
// sets the reference b to point to this new object;
// the Foo class must implement the Cloneable interface
//     for this code to compile
a.x = 5; // modifies the object a
a.x = 5; // modifies the object referenced by a
std::cout << b.x << std::endl;
// outputs 0, because b is
// some object other than a
System.out.println(b.x);
// outputs 0, because b points to
// some object other than a
Foo *c;
// declares c to be a pointer to a
// Foo object (initially
// undefined; could point anywhere)
Foo c;
// declares c to be a reference to a Foo
// object (initially null if c is a class member;
// it is necessary to initialize c before use
// if it is a local variable)
c = new Foo;
// c is set to the value of the address of the Foo object created by operator new
c = new Foo();
// binds c to reference a new Foo object
Foo &d = *c;
// binds d to reference the same object to which c points
Foo d = c;
// binds d to reference the same object as c
c->x = 5;
// modifies the object pointed to by c
c.x = 5;
// modifies the object referenced by c
d.bar(5);  // invokes Foo::bar() for a
c->bar(5); // invokes Foo::bar() for *c
d.bar(5); // invokes Foo.bar() for a
c.bar(5); // invokes Foo.bar() for c
std::cout << d.x << std::endl;
// outputs 5, because d references the
// same object to which c points
System.out.println(d.x);
// outputs 5, because d references the
// same object as c
  • Em C ++, é possível declarar um ponteiro ou referência a um objeto const para evitar que o código do cliente o modifique. Funções e métodos também podem garantir que eles não modificarão o objeto apontado por um ponteiro usando a palavra-chave "const". Isso reforça a correção constante .
  • Em Java, na maior parte, a correção const deve depender da semântica da interface da classe, ou seja, não é fortemente aplicada, exceto para membros de dados públicos que são rotulados final.
C ++ Java
const Foo *a; // it is not possible to modify the object
              // pointed to by a through a
final Foo a; // a declaration of a "final" reference:
             // it is possible to modify the object, 
             // but the reference will constantly point 
             // to the first object assigned to it
a = new Foo();
a = new Foo(); // Only in constructor
a->x = 5;
// ILLEGAL
a.x = 5;
// LEGAL, the object's members can still be modified 
// unless explicitly declared final in the declaring class
Foo *const b = new Foo();
// a declaration of a "const" pointer
// it is possible to modify the object,
// but the pointer will constantly point
// to the object assigned to it here
final Foo b = new Foo();
// a declaration of a "final" reference
b = new Foo();
// ILLEGAL, it is not allowed to re-bind it
b = new Foo();
// ILLEGAL, it is not allowed to re-bind it
b->x = 5;
// LEGAL, the object can still be modified
b.x = 5;
// LEGAL, the object can still be modified
  • C ++ oferece suporte a gotoinstruções, que podem levar à programação de código espaguete . Com exceção da instrução goto (que é muito raramente vista em código real e altamente desencorajada), Java e C ++ têm basicamente as mesmas estruturas de fluxo de controle , projetadas para reforçar o fluxo de controle estruturado e dependem de instruções break e continue para fornecer alguns gotofunções semelhantes. Alguns comentadores apontam que essas instruções de controle de fluxo rotuladas quebram a propriedade de ponto de saída único da programação estruturada.
  • C ++ fornece recursos de baixo nível que geralmente faltam em Java (uma exceção notável é a sun.misc.UnsafeAPI para acesso direto à memória e manipulação). Em C ++, os ponteiros podem ser usados ​​para manipular locais de memória específicos, uma tarefa necessária para escrever componentes de sistema operacional de baixo nível . Da mesma forma, muitos compiladores C ++ oferecem suporte a um montador embutido . O código da linguagem assembly pode ser importado para um programa C e vice-versa. Isso torna a linguagem C ainda mais rápida. Em Java, esse código deve residir em bibliotecas externas e só pode ser acessado por meio da Java Native Interface , com uma sobrecarga significativa para cada chamada.

Semântica

  • C ++ permite valores padrão para argumentos de uma função / método. Java não. No entanto, a sobrecarga de método pode ser usada para obter resultados semelhantes em Java, mas gerar código stub redundante.
  • O mínimo de código necessário para compilar para C ++ é uma função, para Java é uma classe.
  • C ++ permite uma gama de conversões implícitas entre tipos nativos (incluindo algumas conversões de estreitamento) e também permite definir conversões implícitas envolvendo tipos definidos pelo usuário. Em Java, apenas as conversões de ampliação entre tipos nativos são implícitas; outras conversões requerem sintaxe de conversão explícita.
    • Um resultado disto é que, embora as condições de alça ( if, whilee o estado de saída em for) em Java e C ++ tanto esperar uma expressão booleana, código, tal como if(a = 5)irá causar um erro de compilação em Java, porque não há conversão de restrição implícita de int para booleano, mas irá compilar em C ++. Isso é útil se o código foi um erro de digitação e if(a == 5)foi intencional. No entanto, os compiladores C ++ atuais geralmente geram um aviso quando tal atribuição é executada dentro de uma expressão condicional. Da mesma forma, instruções de comparação independentes, por exemplo a==5;, sem um efeito colateral, geralmente levam a um aviso.
  • Para passar parâmetros para funções, C ++ suporta passagem por referência e passagem por valor . Em Java, os parâmetros primitivos são sempre passados ​​por valor. Tipos de classe, tipos de interface e tipos de array são chamados coletivamente de tipos de referência em Java e também são sempre transmitidos por valor.
  • Os tipos integrados Java são de um tamanho e intervalo especificados definidos pela especificação da linguagem. Em C ++, um intervalo mínimo de valores é definido para tipos integrados, mas a representação exata (número de bits) pode ser mapeada para quaisquer tipos nativos preferidos em uma determinada plataforma.
    • Por exemplo, os caracteres Java são caracteres Unicode de 16 bits e as strings são compostas por uma sequência de tais caracteres. C ++ oferece caracteres estreitos e largos, mas o tamanho real de cada um depende da plataforma, assim como o conjunto de caracteres usado. As cordas podem ser formadas de qualquer tipo.
    • Isso também implica que os compiladores C ++ podem selecionar automaticamente a representação mais eficiente para a plataforma de destino (ou seja, inteiros de 64 bits para uma plataforma de 64 bits), enquanto a representação é fixa em Java, o que significa que os valores podem ser armazenados no menor - tamanho eficiente, ou deve preencher os bits restantes e adicionar código para emular o comportamento de largura reduzida.
  • O arredondamento e a precisão de valores de ponto flutuante e operações em C ++ são definidos pela implementação (embora apenas plataformas muito exóticas ou antigas se afastem do padrão IEEE 754 ). Java fornece um modelo opcional de ponto flutuante estrito ( strictfp ) que garante resultados mais consistentes entre as plataformas, embora ao custo de um desempenho de tempo de execução possivelmente mais lento. No entanto, o Java não cumpre estritamente com o padrão IEEE 754. A maioria dos compiladores C ++ irá, por padrão, cumprir parcialmente com IEEE 754 (geralmente excluindo regras estritas de arredondamento e levantando exceções nos resultados NaN), mas fornece opções de conformidade de rigidez variada, para permitir alguma otimização. Se rotularmos essas opções do menos compatível para o mais compatível como rápido , consistente (Java's strictfp ), near-IEEE e strict-IEEE , podemos dizer que a maioria das implementações C ++ padrão para near-IEEE , com opções para alternar para fast ou strict -IEEE -IEEE , enquanto o padrão do Java é rápido com uma opção de alternar para consistente .
  • Em C ++, os ponteiros podem ser manipulados diretamente como valores de endereço de memória. As referências Java são ponteiros para objetos. As referências Java não permitem acesso direto a endereços de memória ou permitem que endereços de memória sejam manipulados com aritmética de ponteiro. Em C ++, pode-se construir ponteiros para ponteiros, ponteiros para ints e duplos, e ponteiros para localizações de memória arbitrárias. As referências Java acessam apenas objetos, nunca primitivos, outras referências ou localizações de memória arbitrárias. Em Java, a memória pode ser lida e gravada por valores arbitrários usando a sun.misc.UnsafeAPI, no entanto, está obsoleta e não é recomendada.
  • Em C ++, os ponteiros podem apontar para funções ou funções de membro ( ponteiros de função ). O mecanismo equivalente em Java usa referências de objeto ou interface.
  • Por meio de objetos alocados em pilha, C ++ oferece suporte ao gerenciamento de recursos com escopo definido , uma técnica usada para gerenciar automaticamente a memória e outros recursos do sistema que oferecem suporte à destruição determinística de objetos. Embora o gerenciamento de recursos com escopo definido em C ++ não possa ser garantido (mesmo objetos com destruidores adequados podem ser alocados usando newe deixados sem exclusão), ele fornece um meio eficaz de gerenciamento de recursos. Recursos compartilhados podem ser gerenciados usando shared_ptr, junto com weak_ptrpara quebrar referências cíclicas. Java suporta gerenciamento automático de memória usando coleta de lixo que pode liberar objetos inacessíveis mesmo na presença de referências cíclicas, mas outros recursos do sistema (arquivos, streams, janelas, portas de comunicação, threads, etc.) devem ser liberados explicitamente porque a coleta de lixo não é garantida para ocorrer imediatamente após a última referência de objeto ser abandonada.
  • C ++ apresenta sobrecarga de operador definida pelo usuário . A sobrecarga de operador permite que tipos definidos pelo usuário ofereçam suporte a operadores (aritméticos, comparações etc.) como tipos primitivos por meio de implementações definidas pelo usuário para esses operadores. Geralmente é recomendado preservar a semântica dos operadores. Java não oferece suporte a nenhuma forma de sobrecarga de operador (embora sua biblioteca use o operador de adição para concatenação de string).
  • Java apresenta suporte de interface de programação de aplicativo (API) padrão para reflexão e carregamento dinâmico de novo código arbitrário.
  • C ++ suporta vinculação estática e dinâmica de binários.
  • Java tem genéricos , cujo objetivo principal é fornecer contêineres de segurança de tipo. C ++ tem modelos de tempo de compilação , que fornecem suporte mais extenso para programação e metaprogramação genérica. Java tem anotações , que permitem adicionar metadados customizados arbitrários a classes e metaprogramação por meio de uma ferramenta de processamento de anotações .
  • Java e C ++ distinguem entre tipos nativos (também chamados de tipos fundamentais ou integrados ) e tipos definidos pelo usuário (também chamados de tipos compostos ). Em Java, os tipos nativos têm apenas semântica de valor e os tipos compostos têm apenas semântica de referência. Em C ++ todos os tipos têm semântica de valor, mas uma referência pode ser criada para qualquer tipo, o que permitirá que o objeto seja manipulado por meio da semântica de referência.
  • C ++ oferece suporte a herança múltipla de classes arbitrárias. Em Java, uma classe pode derivar de apenas uma classe, mas uma classe pode implementar várias interfaces (em outras palavras, ela suporta várias heranças de tipos, mas apenas uma única herança de implementação).
  • Java distingue explicitamente entre interfaces e classes. Em C ++, herança múltipla e funções virtuais puras tornam possível definir classes que funcionam quase como as interfaces Java, com algumas pequenas diferenças.
  • Java tem suporte para linguagem e biblioteca padrão para multi-threading . A synchronized palavra-chave em Java fornece bloqueios mutex simples e seguros para oferecer suporte a aplicativos multithread. Java também fornece bibliotecas robustas e complexas para sincronização multi-threading mais avançada. Somente a partir do C ++ 11 existe um modelo de memória definido para multi-threading em C ++ e suporte de biblioteca para a criação de threads e para muitos primitivos de sincronização. Existem também muitas bibliotecas de terceiros para isso.
  • As funções de membro C ++ podem ser declaradas como funções virtuais , o que significa que o método a ser chamado é determinado pelo tipo de tempo de execução do objeto (também conhecido como despacho dinâmico). Por padrão, os métodos em C ++ não são virtuais (ou seja, opt-in virtual ). Em Java, os métodos são virtuais por padrão, mas podem ser tornados não virtuais usando a finalpalavra - chave (ou seja, opt-out virtual ).
  • Enumerações C ++ são tipos primitivos e suportam conversão implícita em tipos inteiros (mas não de tipos inteiros). Enumerações Java podem ser public static enum{enumName1,enumName2}e são usadas como classes. Outra maneira é fazer outra classe que estende java.lang.Enum<E>) e pode, portanto, definir construtores, campos e métodos como qualquer outra classe. A partir do C ++ 11 , C ++ também oferece suporte a enumerações fortemente tipadas que fornecem mais segurança de tipo e especificação explícita do tipo de armazenamento.
  • Operadores unários '++' e '-': em C ++ "O operando deve ser um lvalue modificável . [Pulado] O resultado é o operando atualizado; é um lvalue ...", mas em Java "a promoção numérica binária mencionado acima pode incluir conversão de unboxing e conversão de conjunto de valores. Se necessário, a conversão de conjunto de valores {e / ou [...] conversão de boxing} é aplicada à soma antes de ser armazenada na variável. ", ou seja, em Java, após a inicialização "Integer i = 2;", "++ i;" muda a referência i atribuindo um novo objeto, enquanto em C ++ o objeto ainda é o mesmo.

Gestão de recursos

  • Java oferece coleta de lixo automática , que pode ser contornada em circunstâncias específicas por meio da especificação Java em tempo real . O gerenciamento de memória em C ++ geralmente é feito por meio de construtores, destruidores e ponteiros inteligentes . O padrão C ++ permite a coleta de lixo, mas não a exige. A coleta de lixo raramente é usada na prática.
  • C ++ pode alocar blocos arbitrários de memória. Java apenas aloca memória por meio da instanciação do objeto. Os blocos de memória arbitrários podem ser alocados em Java como uma matriz de bytes.
  • Java e C ++ usam idiomas diferentes para gerenciamento de recursos. Java depende principalmente da coleta de lixo, que pode recuperar memória, enquanto C ++ depende principalmente do idioma RAII ( Resource Acquisition Is Initialization ). Isso se reflete em várias diferenças entre os dois idiomas:
    • Em C ++, é comum alocar objetos de tipos compostos como variáveis ​​locais ligadas à pilha que são destruídas quando saem do escopo. Em Java, os tipos compostos são sempre alocados no heap e coletados pelo coletor de lixo (exceto em máquinas virtuais que usam análise de escape para converter alocações de heap em alocações de pilha).
    • C ++ tem destruidores, enquanto Java tem finalizadores . Ambos são chamados antes da desalocação de um objeto, mas diferem significativamente. O destruidor de um objeto C ++ deve ser invocado implicitamente (no caso de variáveis ​​ligadas à pilha) ou explicitamente para desalocar um objeto. O destruidor é executado de forma síncrona logo antes do ponto em um programa em que um objeto é desalocado. Assim, a desinicialização e desalocação síncronas e coordenadas em C ++ satisfazem o idioma RAII. Em Java, a desalocação de objetos é manipulada implicitamente pelo coletor de lixo. O finalizador de um objeto Java é chamado de forma assíncrona algum tempo depois de ter sido acessado pela última vez e antes de ser desalocado. Muito poucos objetos precisam de finalizadores. Um finalizador é necessário apenas para objetos que devem garantir alguma limpeza do estado do objeto antes da desalocação, normalmente liberando recursos externos à JVM. Além disso, os finalizadores vêm com graves penalidades de desempenho e aumentam significativamente o tempo que leva para os objetos serem desalocados, portanto, seu uso é desencorajado e obsoleto no Java 9.
    • Com RAII em C ++, um tipo de recurso é normalmente agrupado dentro de uma pequena classe que aloca o recurso na construção e libera o recurso na destruição, e fornece acesso ao recurso entre esses pontos. Qualquer classe que contenha apenas esses objetos RAII não precisa definir um destruidor, pois os destruidores dos objetos RAII são chamados automaticamente quando um objeto dessa classe é destruído. Em Java, a desalocação síncrona segura de recursos pode ser realizada deterministicamente usando a construção try / catch / finally.
    • Em C ++, é possível ter um ponteiro pendente , uma referência obsoleta a um objeto que já foi desalocado. A tentativa de usar um ponteiro pendente normalmente resulta em falha do programa. Em Java, o coletor de lixo não destruirá um objeto referenciado.
    • Em C ++, é possível ter objetos primitivos não inicializados. Java impõe a inicialização padrão.
    • Em C ++, é possível ter um objeto alocado ao qual não há referência válida. Esse objeto inacessível não pode ser destruído (desalocado) e resulta em um vazamento de memória . Em contraste, em Java, um objeto não será desalocado pelo coletor de lixo até que se torne inacessível (pelo programa do usuário). ( Referências fracas são suportados, que trabalham com o coletor de lixo Java para permitir diferentes pontos fortes de acessibilidade.) A coleta de lixo em Java impede que muitos vazamentos de memória, mas vazamentos ainda são possíveis em algumas circunstâncias.

Bibliotecas

  • C ++ fornece acesso de plataforma cruzada a muitos recursos normalmente disponíveis em bibliotecas específicas de plataforma. O acesso direto do Java ao sistema operacional nativo e às funções de hardware requer o uso da Java Native Interface .

Tempo de execução

C ++ Java
C ++ é compilado diretamente para o código de máquina, que é então executado diretamente pela unidade de processamento central . Java é compilado para código de byte que a máquina virtual Java (JVM) então interpreta no tempo de execução. As implementações Java reais fazem a compilação just-in-time para o código de máquina nativo. Como alternativa, o GNU Compiler for Java pode compilar diretamente para o código de máquina.
  • Devido à sua expressividade irrestrita, os recursos da linguagem C ++ de baixo nível (por exemplo, acesso a array não verificado, ponteiros brutos, trocadilhos ) não podem ser verificados de forma confiável em tempo de compilação ou sem sobrecarga em tempo de execução. Erros de programação relacionados podem levar a estouros de buffer de baixo nível e falhas de segmentação . A Biblioteca de modelos padrão fornece abstrações RAII de nível superior (como vetor, lista e mapa) para ajudar a evitar tais erros. Em Java, os erros de baixo nível não podem ocorrer ou são detectados pela máquina virtual Java (JVM) e relatados ao aplicativo na forma de uma exceção .
  • A linguagem Java requer um comportamento específico no caso de um acesso à matriz fora dos limites, o que geralmente requer a verificação dos limites dos acessos à matriz. Isso elimina uma possível fonte de instabilidade, mas geralmente ao custo de diminuir a execução. Em alguns casos, especialmente desde o Java 7, a análise do compilador pode provar uma verificação de limites desnecessária e eliminá-la. C ++ não tem nenhum comportamento necessário para acesso fora dos limites de matrizes nativas, portanto, não requer verificação de limites para matrizes nativas. Coleções de biblioteca padrão C ++ como std :: vector, entretanto, oferecem verificação de limites opcional. Em resumo, os arrays Java são "geralmente seguros; ligeiramente restritos; muitas vezes têm sobrecarga", enquanto os arrays nativos C ++ "têm sobrecarga opcional; são ligeiramente irrestritos; são possivelmente inseguros".

Modelos x genéricos

Tanto C ++ quanto Java fornecem recursos para programação genérica , modelos e genéricos , respectivamente. Embora tenham sido criados para resolver tipos de problemas semelhantes e tenham sintaxe semelhante, eles são bastante diferentes.

Modelos C ++ Java Generics
Classes, funções, apelidos e variáveis ​​podem ser modelados. Classes e métodos podem ser genéricos.
Os parâmetros podem ser variáveis, de qualquer tipo, valor integral, literal de caractere ou um modelo de classe. Os parâmetros podem ser qualquer tipo de referência, incluindo tipos primitivos em caixa (ou seja, inteiro, booleano ...).
Instâncias separadas da classe ou função serão geradas para cada conjunto de parâmetros quando compilado. Para modelos de classe, apenas as funções de membro usadas serão instanciadas. Uma versão da classe ou função é compilada, funciona para todos os parâmetros de tipo (via apagamento de tipo).
Objetos de um modelo de classe instanciado com parâmetros diferentes terão tipos diferentes em tempo de execução (ou seja, instanciações de modelo distintas são classes distintas). Os parâmetros de tipo são apagados quando compilados; objetos de uma classe com parâmetros de tipo diferentes são do mesmo tipo em tempo de execução. Isso causa um construtor diferente. Devido a esse apagamento de tipo, não é possível sobrecarregar métodos usando diferentes instanciações da classe genérica.
A implementação do modelo de classe ou função deve ser visível dentro de uma unidade de tradução para poder ser usada. Isso geralmente implica ter as definições nos arquivos de cabeçalho ou incluídas no arquivo de cabeçalho. A partir do C ++ 11 , é possível usar modelos externos para separar a compilação de algumas instanciações. A assinatura da classe ou função de um arquivo de classe compilado é suficiente para usá-lo.
Os modelos podem ser especializados - uma implementação separada pode ser fornecida para um parâmetro de modelo específico. Os genéricos não podem ser especializados.
Os parâmetros do modelo podem ter argumentos padrão . Antes do C ++ 11 , isso era permitido apenas para classes de modelo, não para funções. Os parâmetros de tipo genérico não podem ter argumentos padrão.
Caracteres curinga não são compatíveis. Em vez disso, os tipos de retorno geralmente estão disponíveis como typedefs aninhados . (Além disso, C ++ 11 adicionou a palavra-chave auto, que atua como um caractere curinga para qualquer tipo que possa ser determinado em tempo de compilação.) Curingas com suporte como parâmetro de tipo.
Não há suporte direto para delimitação de parâmetros de tipo, mas a metaprogramação fornece isso Suporta limitação de parâmetros de tipo com "extends" e "super" para limites superior e inferior, respectivamente; permite a aplicação de relacionamentos entre parâmetros de tipo.
Permite a instanciação de um objeto com o tipo do tipo de parâmetro. Impede a instanciação de um objeto com o tipo do tipo de parâmetro (exceto por meio de reflexão).
O parâmetro de tipo do modelo de classe pode ser usado para métodos e variáveis ​​estáticos. O parâmetro de tipo da classe genérica não pode ser usado para métodos e variáveis ​​estáticos.
Variáveis ​​estáticas não compartilhadas entre classes e funções de parâmetros de tipo diferentes. Variáveis ​​estáticas compartilhadas entre instâncias de classes de diferentes parâmetros de tipo.
Os modelos de classe e função não impõem relações de tipo para parâmetros de tipo em sua declaração. O uso de um parâmetro de tipo incorreto resulta em falha de compilação, geralmente gerando uma mensagem de erro no código do modelo em vez de no código do usuário que o invoca. O uso adequado de classes e funções modeladas depende da documentação adequada. A metaprogramação fornece esses recursos ao custo de um esforço adicional. Houve uma proposição para resolver este problema em C ++ 11 , os chamados Conceitos , que está previsto para o próximo padrão. Classes e funções genéricas podem impor relacionamentos de tipo para parâmetros de tipo em sua declaração. O uso de um parâmetro de tipo incorreto resulta em um erro de tipo no código que o utiliza. Operações em tipos parametrizados em código genérico são permitidas apenas de maneiras que podem ser garantidas como seguras pela declaração. Isso resulta em maior segurança de tipo em detrimento da flexibilidade.
Os modelos são Turing-completos (consulte a metaprogramação de modelos ). Os genéricos também são Turing-completos

Diversos

  • Java e C ++ usam meios diferentes para dividir o código em vários arquivos de origem. Java usa um sistema de pacotes que determina o nome do arquivo e o caminho para todas as definições do programa. Seu compilador importa os arquivos de classe executáveis . C ++ usa um sistema de inclusão de código-fonte de arquivo de cabeçalho para compartilhar declarações entre os arquivos-fonte.
  • Os arquivos de código Java compilados são geralmente menores do que os arquivos de código em C ++, pois o bytecode Java é geralmente mais compacto do que o código de máquina nativo e os programas Java nunca são vinculados estaticamente.
  • A compilação C ++ apresenta uma fase de pré-processamento textual adicionada , enquanto o Java não. Assim, alguns usuários adicionam uma fase de pré-processamento ao processo de construção para melhor suporte à compilação condicional.
  • Os operadores de divisão e módulo do Java são bem definidos para truncar para zero. C ++ (pré- C ++ 11 ) não especifica se esses operadores truncam ou não para zero ou "truncam para -infinito". -3/2 sempre será -1 em Java e C ++ 11, mas um compilador C ++ 03 pode retornar -1 ou -2, dependendo da plataforma. C99 define divisão da mesma maneira que Java e C ++ 11. Ambas as linguagens garantem (onde a e b são tipos inteiros) que (a/b)*b + (a%b) == apara todo a e b (b! = 0). A versão C ++ 03 às vezes será mais rápida, pois é permitido escolher o modo de truncamento nativo do processador.
  • Os tamanhos dos tipos inteiros são definidos em Java (int é de 32 bits, long é de 64 bits), enquanto em C ++ o tamanho dos inteiros e dos ponteiros é o compilador e a interface binária do aplicativo (ABI) dependente de determinadas restrições. Assim, um programa Java terá um comportamento consistente entre plataformas, enquanto um programa C ++ pode exigir adaptação para algumas plataformas, mas pode ser executado mais rápido com tamanhos inteiros mais naturais para a plataforma local.

Um exemplo comparando C ++ e Java existe em Wikibooks .

atuação

Além de executar um programa Java compilado, os computadores que executam aplicativos Java geralmente também devem executar a máquina virtual Java (JVM), enquanto os programas C ++ compilados podem ser executados sem aplicativos externos. As primeiras versões de Java foram significativamente superadas por linguagens compiladas estaticamente, como C ++. Isso ocorre porque as instruções do programa dessas duas linguagens intimamente relacionadas podem compilar para algumas instruções de máquina com C ++, enquanto compila em vários códigos de byte envolvendo várias instruções de máquina, cada um quando interpretado por uma JVM. Por exemplo:

Declaração Java / C ++ Código gerado em C ++ (x86) Código de byte gerado por Java
vector[i]++;
mov edx,[ebp+4h]
mov eax,[ebp+1Ch]
inc dword ptr [edx+eax*4]
aload_1
iload_2
dup2
iaload
iconst_1
Eu adiciono
iastore

Como a otimização de desempenho é uma questão muito complexa, é muito difícil quantificar a diferença de desempenho entre C ++ e Java em termos gerais, e a maioria dos benchmarks não são confiáveis ​​e são tendenciosos. Dadas as naturezas muito diferentes das línguas, diferenças qualitativas definitivas também são difíceis de traçar. Em suma, existem ineficiências inerentes e limites rígidos na otimização em Java, visto que depende fortemente de abstrações flexíveis de alto nível, no entanto, o uso de um compilador JIT poderoso (como nas implementações JVM modernas) pode mitigar alguns problemas. Em qualquer caso, se as ineficiências do Java forem muito grandes, o código C ou C ++ compilado pode ser chamado de Java por meio do JNI.

Algumas ineficiências inerentes à linguagem Java incluem, principalmente:

  • Todos os objetos são alocados no heap. Embora a alocação seja extremamente rápida em JVMs modernos usando 'alocação bump', que executa de forma semelhante à alocação de pilha, o desempenho ainda pode ser impactado negativamente devido à chamada do coletor de lixo. Compiladores JIT modernos atenuam esse problema até certo ponto com análise de escape ou detecção de escape para alocar alguns objetos na pilha, desde o Oracle JDK 6.
  • Projetos de desempenho crítico, como sistemas de banco de dados eficientes e bibliotecas de mensagens, tiveram que usar APIs não oficiais internas, como sun.misc.Unsafeobter acesso ao gerenciamento manual de recursos e ser capaz de fazer a alocação de pilha; efetivamente manipulando pseudo-ponteiros.
  • Uma grande quantidade de conversão em tempo de execução necessária, mesmo usando contêineres padrão, induz a uma penalidade de desempenho. No entanto, a maioria desses casts é eliminada estaticamente pelo compilador JIT.
  • As garantias de segurança têm um custo de tempo de execução. Por exemplo, o compilador deve colocar verificações de intervalo apropriadas no código. Proteger cada acesso à matriz com uma verificação de intervalo não é eficiente, portanto, a maioria dos compiladores JIT tentará eliminá-los estaticamente ou movendo-os para fora dos loops internos (embora a maioria dos compiladores nativos para C ++ faça o mesmo quando as verificações de intervalo são usadas opcionalmente).
  • A falta de acesso a detalhes de baixo nível impede que o desenvolvedor aprimore o programa onde o compilador não pode fazê-lo.
  • O uso obrigatório de semântica de referência para todos os tipos definidos pelo usuário em Java pode introduzir grandes quantidades de indireções de memória supérfluas (ou saltos) (a menos que elidido pelo compilador JIT) que pode levar a perdas frequentes de cache (também conhecido como fragmentação de cache ). Além disso, a otimização de cache, geralmente por meio de algoritmos e estruturas de dados com ou sem cache , pode muitas vezes levar a melhorias de ordens de magnitude no desempenho, bem como evitar degenerescência de complexidade de tempo que é característica de muitos algoritmos de pessimização de cache e é portanto, uma das formas mais importantes de otimização; a semântica de referência, conforme exigido em Java, torna essas otimizações impossíveis de realizar na prática (nem pelo programador nem pelo compilador JIT).
  • Coleta de lixo , pois esta forma de gerenciamento automático de memória introduz sobrecarga de memória.

No entanto, há uma série de benefícios no design do Java, alguns percebidos, outros apenas teorizados:

  • A coleta de lixo Java pode ter melhor coerência de cache do que o uso usual de malloc / new para alocação de memória. No entanto, existem argumentos de que ambos os alocadores fragmentam igualmente o heap e nenhum exibe melhor localidade de cache. No entanto, em C ++, a alocação de objetos únicos no heap é rara, e grandes quantidades de objetos únicos geralmente são alocadas em blocos por meio de um contêiner STL e / ou com um pequeno alocador de objeto.
  • A compilação em tempo de execução pode potencialmente usar informações sobre a plataforma na qual o código está sendo executado para melhorar o código de forma mais eficaz. No entanto, a maioria dos compiladores nativos de última geração (C, C ++, etc.) geram vários caminhos de código para empregar todas as habilidades computacionais de um determinado sistema. Além disso, o argumento inverso pode ser feito de que os compiladores nativos podem explorar melhor a otimização específica da arquitetura e os conjuntos de instruções do que as distribuições JVM multiplataforma.
  • A compilação em tempo de execução permite um inlining de função virtual mais agressivo do que é possível para um compilador estático, porque o compilador JIT tem mais informações sobre todos os destinos possíveis de chamadas virtuais, mesmo se eles estiverem em módulos carregados dinamicamente diferentes. As implementações JVM disponíveis atualmente não têm nenhum problema em inlining da maioria das chamadas monomórficas, principalmente monomórficas e dimórficas, e a pesquisa está em andamento para chamadas inline também megamórficas, graças aos recentes aprimoramentos dinâmicos de invocação adicionados em Java 7. Inlining pode permitir otimizações adicionais, como vetorização ou desenrolamento de loop , resultando em um grande aumento de desempenho geral.
  • Em Java, a sincronização de encadeamentos é incorporada à linguagem, de modo que o compilador JIT pode potencialmente, por meio de análise de escape, elide bloqueios, melhorar significativamente o desempenho de código multi-encadeamento ingênuo.

Além disso, alguns problemas de desempenho ocorrem em C ++:

  • Permitir que os ponteiros apontem para qualquer endereço pode dificultar a otimização devido à possibilidade de aliasing do ponteiro .
  • Uma vez que o código gerado a partir de várias instanciações do mesmo modelo de classe em C ++ não é compartilhado (como com genéricos apagados por tipo em Java), o uso excessivo de modelos pode levar a um aumento significativo do tamanho do código executável ( aumento do código ). No entanto, como os modelos de função são sequencialmente embutidos, eles às vezes podem reduzir o tamanho do código, mas, o que é mais importante, permitem uma análise estática mais agressiva e uma otimização de código pelo compilador, tornando-os mais frequentemente mais eficientes do que o código não padronizado. Em contraste, os genéricos Java são necessariamente menos eficientes do que o código não genérico.
  • Como em um compilador C ++ tradicional, a vinculação dinâmica é executada após a geração e otimização do código em C ++, as chamadas de função que abrangem diferentes módulos dinâmicos não podem ser sequenciadas. No entanto, os compiladores C ++ modernos como o MSVC e o Clang + LLVM oferecem opções de geração de código de tempo de link que permitem que os módulos sejam compilados em formatos intermediários, o que permite o inlining no estágio final do link.

Padrão oficial e referência do idioma

Especificação de linguagem

A linguagem C ++ é definida pela ISO / IEC 14882 , um padrão ISO , que é publicado pelo comitê ISO / IEC JTC1 / SC22 / WG21 . O último rascunho pós-padronização do C ++ 17 também está disponível.

A linguagem C ++ evolui por meio de um comitê gestor aberto denominado Comitê de Padrões C ++. O comitê é composto pelo criador do C ++ Bjarne Stroustrup , o organizador Herb Sutter e outras figuras proeminentes, incluindo muitos representantes de indústrias e grupos de usuários (ou seja, as partes interessadas). Por ser um comitê aberto, qualquer pessoa é livre para ingressar, participar e contribuir com propostas para os próximos lançamentos da norma e das especificações técnicas. O comitê agora tem como objetivo lançar um novo padrão a cada poucos anos, embora no passado processos de revisão rígidos e discussões representassem maiores atrasos entre a publicação de novos padrões (1998, 2003 e 2011).

A linguagem Java é definida pela Java Language Specification , um livro publicado pela Oracle.

A linguagem Java evolui continuamente por meio de um processo denominado Java Community Process , e a comunidade de programação mundial é representada por um grupo de pessoas e organizações - os membros da Comunidade Java - que estão ativamente engajados no aprimoramento da linguagem, enviando solicitações públicas - as Solicitações de Especificação Java - que devem passar por revisões formais e públicas antes de serem integradas à linguagem.

A falta de um padrão firme para Java e a natureza um pouco mais volátil de suas especificações têm sido uma fonte constante de críticas por parte das partes interessadas que desejam mais estabilidade e conservadorismo na adição de novos recursos de linguagem e biblioteca. Em contrapartida, o comitê C ++ também recebe críticas constantes, pelo motivo oposto, ou seja, ser muito rígido e conservador, e demorar muito para lançar novas versões.

Marcas Registradas

"C ++" não é uma marca comercial de nenhuma empresa ou organização e não é propriedade de nenhum indivíduo. "Java" é uma marca comercial da Oracle Corporation .

Referências

links externos