Введение
Сравнение двух языков программирования
Java и C++ – два выдающихся объектно-ориентированных языка программирования. По большинству метрик популярности языков, эти два языка доминировали в объектно-ориентированной и высокопроизводительной разработке программного обеспечения на протяжении значительной части XXI века и часто подвергаются прямому сравнению и сопоставлению. Синтаксис Java был основан на C и C++.
Цели проектирования
Различия между языками программирования C++ и Java можно проследить в их происхождении, поскольку у них разные цели проектирования. C++ был разработан для системного и прикладного программирования (т.е. инфраструктурного программирования), расширяя процедурный язык программирования C, который был разработан для эффективного выполнения. К C, C++ добавил поддержку объектно-ориентированного программирования, обработки исключений, управления ресурсами на основе времени жизни (Resource Acquisition Is Initialization (RAII)), обобщенного программирования, метапрограммирования шаблонов и стандартной библиотеки C++, включающей обобщенные контейнеры и алгоритмы (библиотеку стандартных шаблонов или STL), а также множество других средств общего назначения. Java — это универсальный, параллельный, основанный на классах, объектно-ориентированный язык программирования, предназначенный для минимизации зависимостей от реализации. Он полагается на виртуальную машину Java для обеспечения безопасности и высокой переносимости. Он поставляется с обширной библиотекой, предназначенной для абстрагирования базовой платформы. Java — это язык со статической типизацией, объектно-ориентированный, использующий синтаксис, похожий на (но несовместимый с) C++. Он включает в себя систему документации Javadoc. Различные цели разработки C++ и Java привели к различным принципам и компромиссам в проектировании этих языков. Различия следующие:
C++ | Java
------- | --------
Расширяет C поддержкой объектно-ориентированного и обобщенного программирования. Код на C может быть использован наиболее корректно. | Сильно подвержен влиянию синтаксиса C++/C.
Совместим с исходным кодом C, за исключением некоторых особых случаев. | Совместим с исходным кодом C, за исключением нескольких краевых случаев.
Предоставляет Java Native Interface и, недавно, Java Native Access для прямого вызова кода C/C++. Однако, нативные языки небезопасны, и приложения, использующие нативные методы, подвержены повреждению памяти. Если код написан небрежно, нативные методы могут снизить производительность системы, поскольку сборщик мусора не может контролировать или обслуживать использование нативной памяти, а переключение контекста между нативным и ненативным кодом требует затрат. | Напишите один раз, компилируйте где угодно (WOCA). Напишите один раз, запускайте везде (WORA/WORE).
Позволяет процедурное, функциональное, объектно-ориентированное, обобщенное программирование и метапрограммирование шаблонов. Поддерживает смешение парадигм. | Разрешает процедурное программирование, функциональное программирование (с Java 8) и обобщенное программирование (с Java 5), но настоятельно рекомендует объектно-ориентированную парадигму.
Включает поддержку создания языков сценариев. |
Выполняется как нативный исполняемый машинный код для целевого набора инструкций. | Выполняется на виртуальной машине.
Предоставляет типы объектов и имена типов. |
Позволяет отражение через информацию о типе времени выполнения (RTTI). | Рефлексивен, позволяя метапрограммирование и динамическое генерирование кода во время выполнения.
Имеет несколько стандартов двоичной совместимости (обычно Microsoft (для компилятора MSVC) и Itanium/GNU (для почти всех остальных компиляторов)). | Имеет один стандарт двоичной совместимости, кросс-платформенный для ОС и компилятора.
Необязательная автоматическая проверка границ (например, метод `at` в контейнерах `vector` и `string`). | Все операции должны быть проверены на границы всеми совместимыми дистрибутивами Java. HotSpot может удалить проверку границ.
Нативная поддержка беззнаковой арифметики. | Отсутствует нативная поддержка беззнаковой арифметики. Java 8 изменяет некоторые аспекты, но детали неясны.
Стандартные минимальные пределы для всех числовых типов, но фактические размеры определяются реализацией. Стандартные типы доступны через стандартную библиотеку `<cstdint>`. | Стандартизированные пределы и размеры всех примитивных типов на всех платформах.
Поддерживаются указатели, ссылки и передача по значению для всех типов (примитивных или пользовательских). | Все типы (примитивные и ссылочные) всегда передаются по значению.
Управление памятью может осуществляться вручную через `new`/`delete`, автоматически по области видимости или с помощью умных указателей. Поддерживает детерминированное уничтожение объектов. | Автоматический сбор мусора.
Стандарт ABI сборки мусора в C++11, хотя компиляторы не обязаны реализовывать сборку мусора. | Поддерживает недетерминированный метод `finalize`, использование которого не рекомендуется.
Управление ресурсами может осуществляться вручную или автоматически на основе времени жизни (RAII). | Управление ресурсами обычно должно осуществляться вручную или автоматически с помощью финализаторов, хотя это обычно не рекомендуется.
Имеет `try-with-resources` для автоматического управления ресурсами на основе области видимости (начиная с версии 7). Также может быть выполнено с использованием внутреннего API `sun.misc.Unsafe`, но такое использование настоятельно не рекомендуется и будет заменено общедоступным API в будущей версии Java. |
Поддерживает классы, структуры (типы пассивных данных (PDS)) и объединения, и может выделять их в куче или в стеке. | Классы выделяются в куче. Java SE 6 оптимизирует с помощью анализа экранирования для выделения некоторых объектов в стеке.
Позволяет явно переопределять типы и некоторые неявные сужающие преобразования (для совместимости с C). | Жесткая типобезопасность, за исключением расширяющих преобразований.
Стандартная библиотека C++ была разработана с ограниченной областью применения и функциями, но включает поддержку языка, диагностику, общие утилиты, строки, локали, контейнеры, алгоритмы, итераторы, числовые методы, ввод/вывод, генераторы случайных чисел, разбор регулярных выражений, средства многопоточности, признаки типов (для статической интроспекции типов) и стандартную библиотеку C. Библиотека Boost предлагает больше функций, включая сетевой ввод/вывод. | Богатый набор сторонних библиотек существует для графического интерфейса пользователя и других функций, таких как: Adaptive Communication Environment (ACE), Crypto++, различные библиотеки обмена мгновенными сообщениями (IM) XMPP, OpenLDAP, Qt, gtkmm.
Стандартная библиотека расширялась с каждой версией. К версии 1.6 библиотека включала поддержку локалей, ведения журналов, контейнеров и итераторов, алгоритмов, программирования графического интерфейса пользователя (но не с использованием системного графического интерфейса пользователя), графики, многопоточности, сети, безопасности платформы, интроспекции, динамической загрузки классов, блокирующего и неблокирующего ввода/вывода. Она предоставляла интерфейсы или вспомогательные классы для XML, XSLT, MIDI, подключения к базам данных, служб именования (например, LDAP), криптографии, служб безопасности (например, Kerberos), служб печати и веб-служб. SWT предлагал абстракцию для графических интерфейсов пользователя, специфичных для платформы, но был заменен JavaFX в последних версиях, обеспечивая ускорение графики и темы оформления CSS. Хотя он не поддерживает поддержку "родного внешнего вида платформы". |
Перегрузка операторов для большинства операторов. Сохранение значения (семантики) настоятельно рекомендуется. | Операторы не могут быть перегружены. Язык переопределяет `+` и `+=` для класса `String`.
Одиночное и множественное наследование классов, включая виртуальное наследование. | Поддерживает только одиночное наследование классов.
Шаблонные вычисления во время компиляции. |
Позволяет выполнять метапрограммирование, полное по Тьюрингу. | Обобщения используются для достижения базовой параметризации типов, но они не транслируются из исходного кода в байт-код из-за использования стирания типов компилятором.
Указатели на функции, функциональные объекты, лямбда-выражения (в C++11) и интерфейсы (с использованием абстрактных классов). | Функциональные ссылки, функциональные объекты и лямбда-выражения были добавлены в Java 8.
Классы (и интерфейсы, которые являются классами) могут передаваться по ссылке через `SomeClass.class` и `someObject.getClass`. |
Отсутствует стандартный механизм встроенной документации. Существуют сторонние инструменты (например, Doxygen). | Обширная стандартная документация Javadoc для всех системных классов и методов.
Ключевое слово `const` для определения неизменяемых переменных и функций-членов, которые не изменяют объект. Неизменяемость распространяется как средство обеспечения корректности кода относительно изменяемости объектов (см. const correctness). | `final` предоставляет версию `const`, эквивалентную указателям `type* const` для объектов и `const` для примитивных типов.
Неизменяемость членов объекта достигается с помощью интерфейсов только для чтения и инкапсуляции объектов. |
Поддерживает оператор `goto`. | Поддерживает метки с циклами и блоками операторов. `goto` является зарезервированным ключевым словом, но помечено как "неиспользуемое" в спецификации Java.
Семантика
C++ допускает значения по умолчанию для аргументов функции/метода. Java этого не поддерживает. Однако, перегрузка методов может быть использована для получения схожих результатов в Java, но приводит к генерации избыточного шаблонного кода. Минимальный код, необходимый для компиляции в C++ – это функция, а в Java – класс. C++ позволяет широкий спектр неявных преобразований между встроенными типами (включая некоторые сужающие преобразования), а также позволяет определять неявные преобразования, включающие пользовательские типы. В Java неявными являются только расширяющие преобразования между встроенными типами; другие преобразования требуют явного приведения типов. В результате, хотя условия циклов (if, while и условие выхода в for) в Java и C++ ожидают булево выражение, код вида `if(a = 5)` вызовет ошибку компиляции в Java, поскольку отсутствует неявное сужающее преобразование из int в boolean, но будет успешно скомпилирован в C++. Это может быть полезно, если в коде допущена опечатка и подразумевалось `if(a == 5)`. Однако, современные компиляторы C++ обычно генерируют предупреждение при выполнении такого присваивания в условном выражении. Аналогично, отдельные операторы сравнения, например `a==5;`, без побочных эффектов, как правило, приводят к предупреждению. Для передачи параметров функциям, C++ поддерживает как передачу по ссылке, так и передачу по значению. В Java примитивные параметры всегда передаются по значению. Типы классов, интерфейсов и массивов коллективно называются ссылочными типами в Java и также всегда передаются по значению. Встроенные типы Java имеют заданный размер и диапазон, определенные спецификацией языка. В C++ минимальный диапазон значений определен для встроенных типов, но точное представление (количество бит) может быть сопоставлено с предпочтительными встроенными типами на данной платформе. Например, символы Java – это 16-битные символы Unicode, а строки состоят из последовательности таких символов. C++ предлагает как узкие, так и широкие символы, но фактический размер каждого из них зависит от платформы, как и используемый набор символов. Строки могут быть сформированы из любого типа. Это также означает, что компиляторы C++ могут автоматически выбирать наиболее эффективное представление для целевой платформы (например, 64-битные целые числа для 64-битной платформы), в то время как представление фиксировано в Java, что означает, что значения могут быть либо сохранены в менее эффективном размере, либо необходимо дополнять оставшиеся биты и добавлять код для эмуляции поведения с уменьшенной шириной. Округление и точность значений с плавающей точкой и операций в C++ определяются реализацией (хотя только очень экзотические или устаревшие платформы отклоняются от стандарта IEEE 754). Java предоставляет необязательную строгую модель с плавающей точкой (strictfp), которая гарантирует более согласованные результаты на разных платформах, хотя и за счет возможного снижения производительности во время выполнения. Однако Java не полностью соответствует стандарту IEEE 754. Большинство компиляторов C++ по умолчанию частично соответствуют IEEE 754 (обычно исключая строгие правила округления и генерируя исключения при результатах NaN), но предоставляют опции соответствия различной строгости, чтобы обеспечить некоторую оптимизацию. Если обозначить эти опции от наименее совместимых к наиболее совместимым как быстрые, согласованные (Java's strictfp), близкие к IEEE и строгие IEEE, можно сказать, что большинство реализаций C++ по умолчанию близки к IEEE, с возможностью переключения на быстрые или строгие IEEE, в то время как Java по умолчанию использует быстрые с возможностью переключения на согласованные. В C++ указателями можно манипулировать напрямую как значениями адресов памяти. Ссылки Java – это указатели на объекты. Ссылки Java не позволяют прямого доступа к адресам памяти или манипулирования адресами памяти с помощью арифметики указателей. В C++ можно создавать указатели на указатели, указатели на int и double, а также указатели на произвольные места в памяти. Ссылки Java получают доступ только к объектам, никогда к примитивам, другим ссылкам или произвольным местам в памяти. В Java память может быть прочитана и записана произвольными значениями с помощью API `sun.misc.Unsafe`, однако он устарел и не рекомендуется к использованию. В C++ указатели могут указывать на функции или методы класса (указатели на функции). Эквивалентный механизм в Java использует ссылки на объекты или интерфейсы. С помощью объектов, выделенных в стеке, C++ поддерживает управление ресурсами на основе области видимости, технику, используемую для автоматического управления памятью и другими системными ресурсами, обеспечивающую детерминированное уничтожение объектов. Хотя управление ресурсами на основе области видимости в C++ не может быть гарантировано (даже объекты с корректными деструкторами могут быть выделены с помощью `new` и оставлены без удаления), оно обеспечивает эффективный способ управления ресурсами. Общие ресурсы могут управляться с помощью `shared_ptr`, а также `weak_ptr` для разрыва циклических ссылок. Java поддерживает автоматическое управление памятью с помощью сборщика мусора, который может освобождать недостижимые объекты даже при наличии циклических ссылок, но другие системные ресурсы (файлы, потоки, окна, порты связи, потоки и т.д.) должны быть явно освобождены, поскольку сборка мусора не гарантируется произойти сразу после того, как последняя ссылка на объект будет отброшена. C++ поддерживает перегрузку операторов, определяемых пользователем. Перегрузка операторов позволяет пользовательским типам поддерживать операторы (арифметические, сравнения и т.д.) как встроенные типы посредством пользовательских реализаций этих операторов. Обычно рекомендуется сохранять семантику операторов. Java не поддерживает перегрузку операторов (хотя ее библиотека использует оператор сложения для конкатенации строк). Java предоставляет стандартную поддержку API для рефлексивного программирования (reflection) и динамической загрузки произвольного нового кода. C++ поддерживает статическую и динамическую компоновку двоичных файлов. Java имеет обобщения (generics), основная цель которых – предоставление типобезопасных контейнеров. C++ имеет шаблоны времени компиляции (compile-time templates), которые обеспечивают более широкую поддержку обобщенного программирования и метапрограммирования. Java имеет аннотации, которые позволяют добавлять произвольные пользовательские метаданные к классам и метапрограммирование с помощью инструмента обработки аннотаций. И Java, и C++ различают встроенные типы (также называемые фундаментальными или базовыми типами) и пользовательские типы (также называемые составными типами). В Java встроенные типы имеют только семантику значения, а составные типы – только семантику ссылки. В C++ все типы имеют семантику значения, но для любого типа можно создать ссылку, которая позволит манипулировать объектом с помощью семантики ссылки. C++ поддерживает множественное наследование произвольных классов. В Java класс может наследовать только от одного класса, но класс может реализовывать несколько интерфейсов (другими словами, он поддерживает множественное наследование типов, но только единичное наследование реализации). Java явно различает интерфейсы и классы. В C++ множественное наследование и виртуальные функции позволяют определять классы, которые функционируют почти как интерфейсы Java, с некоторыми небольшими различиями. Java имеет поддержку многопоточности как на уровне языка, так и стандартной библиотеки. Ключевое слово `synchronized` в Java предоставляет мьютексы для поддержки многопоточных приложений. Java также предоставляет библиотеки для более продвинутой синхронизации многопоточности. C++11 имеет определенную модель памяти для многопоточности в C++ и поддержку библиотек для создания потоков и многих примитивов синхронизации. Существует также множество сторонних библиотек для этого. Методы класса C++ могут быть объявлены как виртуальные, что означает, что метод, который будет вызван, определяется типом объекта во время выполнения (также известный как динамическая диспетчеризация). По умолчанию методы в C++ не являются виртуальными (т.е. включение виртуальности выполняется явно). В Java методы являются виртуальными по умолчанию, но могут быть сделаны не виртуальными с помощью ключевого слова `final` (т.е. отключение виртуальности выполняется явно). Перечисления C++ являются примитивными типами и поддерживают неявное преобразование в целочисленные типы (но не из целочисленных типов). Перечисления Java могут быть объявлены как `public static enum{enumName1,enumName2}` и используются как классы. Другой способ – создать другой класс, который расширяет `java.lang.Enum<E>` и, следовательно, может определять конструкторы, поля и методы, как и любой другой класс. Начиная с C++11, C++ поддерживает строго...
Управление ресурсами
Java предлагает автоматическую сборку мусора, которую можно обойти в определенных обстоятельствах с помощью спецификации Real Time Java. Управление памятью в C++ обычно осуществляется через конструкторы, деструкторы и умные указатели. Стандарт C++ допускает сборку мусора, но не требует ее. На практике сборка мусора используется редко. C++ может выделять произвольные блоки памяти. Java выделяет память только при создании объектов. В Java произвольные блоки памяти могут быть выделены в виде массива байтов. Java и C++ используют различные идиомы для управления ресурсами. Java в основном полагается на сборку мусора, которая может освободить память, в то время как C++ в основном полагается на идиому Resource Acquisition Is Initialization (RAII). Это отражается в нескольких различиях между двумя языками: в C++ обычно выделяют объекты составных типов как локальные переменные, связанные со стеком, которые уничтожаются при выходе из области видимости. В Java составные типы всегда выделяются в куче и собираются сборщиком мусора (за исключением виртуальных машин, использующих анализ экранирования для преобразования выделений в куче в выделения в стеке). В C++ есть деструкторы, а в Java – финализаторы. Оба вызываются перед освобождением объекта, но существенно отличаются. Деструктор объекта C++ должен вызываться неявно (в случае переменных, связанных со стеком) или явно для освобождения объекта. Деструктор выполняется синхронно непосредственно перед точкой в программе, в которой объект освобождается. Синхронное, скоординированное прекращение инициализации и освобождение в C++ таким образом соответствуют идиоме RAII. Деструкторы в C++ – это нормальный способ возврата ресурсов, связанных с объектом, и являются необходимой противоположностью конструкторов. В Java освобождение объектов обрабатывается неявно сборщиком мусора. Финализатор объекта Java вызывается асинхронно через некоторое время после последнего доступа к нему и перед его освобождением. Очень немногим объектам нужны финализаторы. Финализатор необходим только для объектов, которые должны гарантировать некоторую очистку состояния объекта перед освобождением, обычно освобождая ресурсы, внешние для JVM. Прямое использование финализаторов обычно не рекомендуется, поскольку они непредсказуемы, обычно опасны и в большинстве случаев не нужны. Следует избегать рассмотрения финализаторов как аналогов деструкторов C++. Скорее, конструкция try-with-resources или блок try-finally достигают более схожей цели. Одна из проблем с финализаторами или очистителями заключается в том, что нет гарантии их немедленного выполнения. Следовательно, финализатор никогда не следует использовать для задач, критичных ко времени. Кроме того, финализаторы приводят к серьезным штрафам производительности и значительно увеличивают время, необходимое для освобождения объектов, поэтому их использование не рекомендуется и устарело в Java 9. При использовании RAII в C++ один тип ресурса обычно оборачивается в небольшой класс, который выделяет ресурс при создании и освобождает его при уничтожении, обеспечивая доступ к ресурсу между этими моментами. Любой класс, содержащий только такие объекты RAII, не нуждается в определении деструктора, поскольку деструкторы объектов RAII вызываются автоматически при уничтожении объекта этого класса. В Java безопасное синхронное освобождение ресурсов можно выполнить детерминированно с помощью конструкции try-catch-finally. В качестве альтернативы, конструкция try-with-resources, представленная в Java 7, должна использоваться вместо конструкции try-finally. Конструкция try-with-resources более лаконична и удобочитаема. Она также предоставляет более полезную диагностическую информацию, поскольку подавленные исключения не отбрасываются и будут напечатаны в трассировке стека с указанием того, что они были подавлены. В C++ возможен висячий указатель – устаревшая ссылка на объект, который уже был освобожден. Попытка использовать висячий указатель обычно приводит к сбою программы. В Java сборщик мусора не уничтожает объект, на который есть ссылка. В C++ возможны неинициализированные примитивные объекты. Java обеспечивает инициализацию по умолчанию. В C++ возможен выделенный объект, к которому нет действительной ссылки. Такой недоступный объект не может быть уничтожен (освобожден), что приводит к утечке памяти. В отличие от этого, в Java объект не будет освобожден сборщиком мусора, пока он не станет недоступным (из пользовательской программы). (Поддерживаются слабые ссылки, которые работают со сборщиком мусора Java, чтобы обеспечить различные уровни доступности). Сборка мусора в Java предотвращает многие утечки памяти, но утечки все же возможны в определенных обстоятельствах. Автоматический сборщик мусора может создать ложное впечатление, что в Java не нужно думать об управлении памятью. Однако это не совсем так. В широком смысле, это связано с тем, что программа может иметь «утечки памяти», более формально известные как «непреднамеренное удержание объектов». Примером утечки памяти может быть программа, написанная без логических ошибок, за исключением того, что она не удалила устаревшие ссылки. Это приводит к более высокой активности сборщика мусора, большему объему используемой памяти. В крайних случаях эта проблема может привести к ошибке OutOfMemoryError, но это случается редко. Решением является обнуление ссылок на объекты. Вторая распространенная причина утечек памяти – использование кэша, который больше не актуален. Решением для утечек памяти, вызванных использованием устаревшего кэша, является представление кэша с помощью WeakHashMap.
In C++ it is common to allocate objects of compound types as local stack bound variables which are destroyed when they go out of scope. In Java compound types are always allocated on the heap and collected by the garbage collector (except in virtual machines that use escape analysis to convert heap allocations to stack allocations). C++ has destructors, while Java has finalizers. Both are invoked before an object's deallocation, but they differ significantly. A C++ object's destructor must be invoked implicitly (in the case of stack bound variables) or explicitly to deallocate an object. The destructor executes synchronously just before the point in a program at which an object is deallocated. Synchronous, coordinated uninitializing and deallocating in C++ thus satisfy the RAII idiom. Destructors in C++ is the normal way of getting back the resources associated with an object, and is a needed counterpart to constructors. In Java, object deallocation is implicitly handled by the garbage collector. A Java object's finalizer is invoked asynchronously some time after it has been accessed for the last time and before it is deallocated. Very few objects need finalizers. A finalizer is needed by only objects that must guarantee some cleanup of the object state before deallocating, typically releasing resources external to the JVM. Direct usages of finalizers are usually not advised, as they are unpredictable, usually dangerous, and most of the time unneeded. One has to be cautious not to think of finalizers as C++ destructors. Rather, the try with resources or try finally block achieves a more similar purpose as the destructor. One problem with finalizers or cleaners is that it is not guaranteed that they will run immediately. Hence, a finalizer should never be used for tasks that are time critical. Additionally, finalizers come with severe performance penalties and significantly increase the time it takes for objects to be deallocated, so their use is discouraged and deprecated in Java 9. With RAII in C++, one type of resource is typically wrapped inside a small class that allocates the resource upon construction and releases the resource upon destruction, and provide access to the resource in between those points. Any class that contain only such RAII objects do not need to define a destructor since the destructors of the RAII objects are called automatically as an object of this class is destroyed. In Java, safe synchronous deallocation of resources can be performed deterministically using the try/catch/finally construct. Alternatively, the try with resources construct, which was introduced in Java 7, should be used in preference to try finally construct. The try with resources construct is more concise and readable. It also provide more helpful diagnostic information, since suppressed exception are not discarded, and will be printed in the stack trace with information saying that they were suppressed. In C++, it is possible to have a dangling pointer, a stale reference to an object that has already been deallocated. Attempting to use a dangling pointer typically results in program failure. In Java, the garbage collector will not destroy a referenced object. In C++, it is possible to have uninitialized primitive objects. Java enforces default initialization. In C++, it is possible to have an allocated object to which there is no valid reference. Such an unreachable object cannot be destroyed (deallocated), and results in a memory leak. In contrast, in Java an object will not be deallocated by the garbage collector until it becomes unreachable (by the user program). (Weak references are supported, which work with the Java garbage collector to allow for different strengths of reachability.) Garbage collection in Java prevents many memory leaks, but leaks are still possible under some circumstances. The automatic garbage collector may give the false impression that in Java one does not need to think about memory management. However this is not quite true. Loosely speaking, this is because a program can have "memory leaks", more formally known as "unintentional object retentions". An example of a memory leak that may occur is for a program that has been written without any logical errors, except that it did not eliminate obsolete references. This results in higher use of garbage collector activity, higher memory footprint. In extreme circumstances, this problem can lead to an OutOfMemoryError, but this rarely happens. The solution to this is to null out object references. A second common reason for memory leak is the use of cache that has become no longer relevant. The solution to memory leaks due to using old cache is to represent the cache using a WeakHashMap.
Библиотеки
C++ предоставляет кроссплатформенный доступ ко многим функциям, которые обычно доступны в библиотеках, специфичных для платформы. Прямой доступ из Java к функциям операционной системы и аппаратному обеспечению требует использования Java Native Interface (JNI).
Время выполнения
C++ Java C++ компилируется непосредственно в машинный код, который затем выполняется непосредственно центральным процессором. Java компилируется в байт-код, который виртуальная машина Java (JVM) интерпретирует во время выполнения. Реальные реализации Java используют JIT-компиляцию (компиляцию "на лету") в машинный код. Из-за своей неограниченной выразительности, низкоуровневые возможности языка C++ (например, неконтролируемый доступ к массивам, "сырые" указатели, приведение типов) не могут быть надёжно проверены во время компиляции или без дополнительных затрат во время выполнения. Ошибки программирования, связанные с этим, могут приводить к переполнению буфера и ошибкам сегментации. Стандартная библиотека шаблонов предоставляет абстракции RAII более высокого уровня (например, vector, list и map), чтобы помочь избежать подобных ошибок. В Java низкоуровневые ошибки либо не могут возникнуть, либо обнаруживаются виртуальной машиной Java (JVM) и сообщаются приложению в виде исключения. Язык Java требует определённого поведения при обращении к массиву за его пределами, что обычно подразумевает проверку границ доступа к массиву. Это устраняет потенциальный источник нестабильности, но обычно за счёт снижения производительности. В некоторых случаях, особенно начиная с Java 7, анализ компилятора может доказать ненужность проверки границ и исключить её. В C++ не определено поведение при обращении к нативным массивам за их пределами, поэтому проверка границ для них не требуется. Однако коллекции стандартной библиотеки C++, такие как std::vector, предлагают опциональную проверку границ. В итоге, массивы Java – "обычно безопасны, немного ограничены, часто имеют накладные расходы", а нативные массивы C++ – "имеют опциональные накладные расходы, немного не ограничены, потенциально небезопасны".
Разное
Java и C++ используют различные механизмы для разделения кода на несколько исходных файлов. Java использует систему пакетов, которая определяет имя файла и путь для всех определений программы. Его компилятор импортирует исполняемые файлы классов. C++ использует систему включения файлов заголовков для обмена объявлениями между исходными файлами. Скомпилированные файлы кода Java обычно меньше, чем файлы кода в C++, поскольку байт-код Java, как правило, более компактен, чем машинный код, а программы Java никогда не статически линкуются. Компиляция в C++ включает в себя дополнительную фазу текстовой предварительной обработки, в то время как в Java такой фазы нет. Поэтому некоторые пользователи добавляют фазу предварительной обработки в свой процесс сборки для лучшей поддержки условной компиляции. Операторы деления и взятия остатка в Java чётко определены и всегда усекают результат к нулю. C++ (до C++11) не определяет, усекают ли эти операторы к нулю или "к минус бесконечности". 3/2 всегда будет равно 1 в Java и C++11, но компилятор C++03 может вернуть либо 1, либо 2, в зависимости от платформы. C99 определяет деление аналогично Java и C++11. Оба языка гарантируют (где a и b – целочисленные типы), что (a/b)*b + (a%b) == a для всех a и b (b != 0). C++03 может быть быстрее, поскольку ему разрешено выбирать режим усечения, который является нативным для процессора. Размеры целочисленных типов определены в Java (int – 32 бита, long – 64 бита), в то время как в C++ размер целых чисел и указателей зависит от компилятора и ABI (Application Binary Interface) в заданных пределах. Таким образом, программа Java будет иметь согласованное поведение на разных платформах, в то время как программе C++ может потребоваться адаптация для некоторых платформ, но она может работать быстрее, используя более естественные размеры целых чисел для конкретной платформы. Пример сравнения C++ и Java можно найти в Wikibooks.
Языковые спецификации
Язык C++ определяется стандартом ISO/IEC 14882, опубликованным комитетом ISO/IEC JTC1/SC22/WG21. Также доступен последний, пост-стандартизационный проект C++17. Развитие языка C++ осуществляется посредством открытого руководящего комитета, называемого Комитетом по стандартам C++. В состав комитета входят создатель C++ Бьярне Строуструп, председатель Герб Саттер и другие известные специалисты, включая многочисленных представителей индустрии и пользовательских групп (то есть заинтересованных сторон). Будучи открытым комитетом, любой желающий может присоединиться к его работе, участвовать в обсуждениях и вносить предложения для будущих версий стандарта и технических спецификаций. В настоящее время комитет стремится выпускать новый стандарт каждые несколько лет, хотя в прошлом строгие процедуры рассмотрения и обсуждения приводили к более длительным задержкам между публикациями новых стандартов (1998, 2003 и 2011 годов). Язык Java определяется спецификацией языка Java – книгой, издаваемой компанией Oracle. Язык Java непрерывно развивается посредством процесса, называемого Java Community Process, а мировое сообщество программистов представлено группой людей и организаций – членами Java Community, которые активно участвуют в совершенствовании языка, направляя публичные запросы (Java Specification Requests), которые должны пройти формальную и публичную экспертизу, прежде чем будут включены в язык. Отсутствие чёткого стандарта для Java и несколько более неустойчивый характер его спецификаций постоянно подвергаются критике со стороны заинтересованных сторон, стремящихся к большей стабильности и консервативности при добавлении новых функций языка и библиотек. В отличие от этого, комитет C++ также постоянно подвергается критике, но по противоположной причине – за излишнюю строгость и консерватизм, а также за длительные сроки выпуска новых версий.
Торговые марки
"C++" не является товарным знаком какой-либо компании или организации и не принадлежит какому-либо лицу. "Java" является товарным знаком корпорации Oracle.