言語機能
構文 Javaの構文は 文脈自由文法 を採用しており、シンプルなLALRパーサー で解析できます。C++の解析はより複雑です。例えば、は が変数でFoo<1>(3); あれば比較のシーケンスになりますが、 がクラステンプレートの名前でFooあればオブジェクトを作成します。FooC++では、名前空間レベルの定数、変数、関数が使用できます。Javaでは、これらのエンティティは特定の型に属する必要があり、そのため、クラスまたはインターフェース といった型定義内で定義する必要があります。 Javaでは、すべての型は名前空間 (Javaパッケージ )に属し、C++のように名前空間に属さないコードがグローバル名前空間の一部となるような「グローバル名前空間」は存在しません(コードがどのクラスにも属していないように見えるコンパクトなソースファイルであっても、そのようなコードは最終的に無名パッケージ内のトップレベルクラスに属します)。Javaでは、「無名パッケージ」はグローバル名前空間ではなく、その中のクラスは名前付きパッケージ内のコードからインポートすることはできません。 Javaには厳密なグローバル継承ツリー があり、すべてのクラスは(暗黙的に)最上位型 を継承します。C java.lang.Object++にはグローバル継承ツリーはありません。最上位型はありますが、継承型ではなく、型消去された std::anyランタイムオブジェクトです。 C++ではオブジェクトは値ですが、Javaではそうではありません。C++はデフォルトで値セマンティクスを使用しますが、Javaは常に 参照セマンティクス を使用します。C++で参照セマンティクスを選択するには、ポインタまたは参照のいずれかを使用できます。 C++ は文をサポートしていますが、これはスパゲッティコード goto プログラミングにつながる可能性があります。goto 文 (実際のコードではほとんど見られず、強く推奨されません) を除けば、Java と C++ は基本的に同じ制御フロー 構造を持ち、構造化された制御フローを強制するように設計されており、break 文と continue 文を使用していくつかの類似の機能を提供します。一部のコメントでは、これらのラベル付きフロー制御文が構造化プログラミングの単一出口特性を破ると指摘されています。[ 11 ] goto C++は、Javaにはほとんどない低レベル機能を提供します(ただし、sun.misc.Unsafeメモリへの直接アクセスと操作のための内部APIは例外です)。C++では、ポインタを使用して特定のメモリ位置を操作できます。これは、低レベルのオペレーティングシステム コンポーネントを作成する際に必要な機能です。同様に、多くのC++コンパイラはインラインアセンブラを サポートしており、アセンブリ関数をC/C++プログラムにリンクできます。Javaでは、このようなコードは外部ライブラリに格納する必要があり、Java Native Interface を介してのみアクセスできますが、呼び出しごとにかなりのオーバーヘッドが発生します。
意味論 C++では関数/メソッドの引数にデフォルト値を設定できますが、Javaではできません。ただし、 Javaではメソッドのオーバーロードを 使用することで同様の結果を得ることができますが、冗長なスタブコードが生成されます。 C++のコンパイルに必要な最小限のコードは関数であり、Javaの場合はクラスです。しかし、Java 21以降、無名クラスが導入されたことで、main関数のみで構成されるJavaプログラムを作成することが可能になりました。 C++では、ネイティブ型間の暗黙的な型変換(一部の縮小型変換を含む)が幅広く許可されており、ユーザー定義型を含む暗黙的な型変換を定義することもできます。Javaでは、ネイティブ型間の拡大型変換のみが暗黙的に許可されており、その他の型変換には明示的なキャスト構文が必要です。 その結果、Java と C++ のループ条件 ( if、whileおよび の終了条件for) はどちらもブール式を期待しますが、 のようなコードは、int から Boolean への暗黙的な縮小変換がないため Java ではコンパイル エラーになりますが、C++ ではコンパイルされます。これは、コードがタイプミスで意図的なものであった場合に便利です。ただし、現在の C++ コンパイラは、条件式内でこのような代入が行われると、通常は警告を生成します。同様に、副作用のない単独の比較文 ( など) も通常は警告につながります。if ( a = 5 ) if ( a == 5 ) a == 5; 関数にパラメータを渡す場合、C++ は参照渡し と値渡しの 両方をサポートしています。Java では、プリミティブ型のパラメータは常に値渡しされます。クラス型、インターフェース型、配列型は、Java ではまとめて参照型と呼ばれ、常に値渡しされます。[ 12 ] [ 13 ] [ 14 ] Javaの組み込み型は、言語仕様で定義された特定のサイズと範囲を持ちます。C++では、組み込み型に対して最小値の範囲が定義されていますが、正確な表現(ビット数)は、特定のプラットフォームで優先されるネイティブ型にマッピングできます。 例えば、Javaの文字は16ビットUnicode 文字であり、文字列はそのような文字の連続で構成されます。C++は狭幅文字と広幅文字の両方を提供しますが、それぞれの実際のサイズはプラットフォームに依存し、使用される文字セットも同様です。文字列はどちらのタイプからも作成できます。 これはまた、C++コンパイラがターゲットプラットフォームに対して最も効率的な表現(例えば、64ビットプラットフォームの場合は64ビット整数)を自動的に選択できるのに対し、Javaでは表現が固定されているため、値は効率の悪いサイズで格納されるか、残りのビットをパディングして、縮小幅の動作をエミュレートするコードを追加する必要があることを意味します。 C++ の浮動小数点値と演算の丸めと精度は実装依存です (ただし、IEEE 754 標準から逸脱するのは非常に特殊なプラットフォームまたは古いプラットフォームのみです)。Java は、実行時のパフォーマンスが低下する可能性があるものの、プラットフォーム間でより一貫した結果を保証するオプションの厳密浮動小数点モデル ( strictfp ) を提供します。ただし、Java は IEEE 754 標準に厳密には準拠していません。ほとんどの C++ コンパイラは、デフォルトでは IEEE 754 に部分的に準拠しますが (通常は厳密な丸めルールを除外し、NaN の結果に対して例外を発生させます)、最適化を可能にするために、さまざまな厳密さの準拠オプションを提供します。[ 15 ] [ 16 ] これらのオプションを準拠度の低いものから高いものへとfast 、consistent (Java のstrictfp )、near-IEEE 、strict-IEEE とラベル付けすると、ほとんどの C++ 実装はデフォルトでnear-IEEEであり、 fast またはstrict-IEEE に切り替えるオプションがあるのに対し、Java はデフォルトでfastであり、 consistent に切り替えるオプションがあると言えます。 C++ では、ポインタは メモリ アドレス値として直接操作できます。Java の参照はオブジェクトへのポインタです。[ 17 ] Java の参照では、メモリ アドレスへの直接アクセスや、ポインタ演算によるメモリ アドレスの操作はできません。C++ では、ポインタへのポインタ、int 型と double 型へのポインタ、任意のメモリ位置へのポインタを作成できます。Java の参照はオブジェクトにのみアクセスし、プリミティブ型、他の参照、任意のメモリ位置にはアクセスしません。Java では、API を使用して任意の値でメモリを読み書きできますがsun.misc.Unsafe、これは非推奨であり、推奨されません。 C++では、ポインタは関数またはメンバ関数(関数ポインタ )を指すことができます。Javaにおける同等の仕組みは、オブジェクト参照またはインターフェース参照を使用します。C++ はスタックに割り当てられたオブジェクトを介してスコープ付きリソース管理 をサポートしています。これは、メモリやその他のシステム リソースを自動的に管理し、決定論的なオブジェクト破棄をサポートする手法です。C++ のスコープ付きリソース管理は保証できません (適切なデストラクタを持つオブジェクトでも を使用して割り当てられnew、削除されないままになる可能性があります) が、効果的なリソース管理手段を提供します。共有リソースは を使用して管理できstd::shared_ptr<T>、循環参照を解消するには を使用します。Java は、ガベージ コレクション [ 7 ] std::weak_ptr<T>を使用した自動メモリ管理をサポートしており、循環参照が存在する場合でも到達不能なオブジェクトを解放できますが、ガベージ コレクションは最後のオブジェクト参照が破棄された直後に発生することが保証されていないため、他のシステム リソース (ファイルストリーム、ウィンドウ、通信ポート、スレッドなど) は明示的に解放する必要があります。Java は、を実装する型に対して -with-resources ブロックを介してスコープ リソース管理を許可しますが、C++ とは異なり、オブジェクトはメモリから破棄されず、所有するリソースのみがメソッドによって破棄されます。tryjava.lang.AutoCloseableclose() C++ には、ユーザー定義演算子オーバーロード 機能があります。演算子オーバーロードにより、ユーザー定義型は、これらの演算子のユーザー定義実装を介して、プリミティブ型と同様に演算子(算術演算子、比較演算子など)をサポートできます。一般的には、演算子のセマンティクスを維持することが推奨されます。Java は、いかなる形式の演算子オーバーロードもサポートしていません(ただし、Java ライブラリでは文字列連結に加算演算子を使用しています)。 Javaは、リフレクションプログラミング (リフレクション)と任意の新規コードの動的ロードのための標準的な アプリケーションプログラミングインターフェース (API)をサポートしています。 C++はバイナリの静的リンクと動的リンクをサポートしています。 JavaとC++(C++26以降)はどちらもリフレクション を提供しており、ある程度のコード内省が可能となっている。Javaのリフレクションは主に実行時に動作し、主に振る舞いを制御するものである一方、C++のリフレクションはコンパイル時に動作し、メタオブジェクトを生成するため、主に生成的な性質を持つ。 Javaにはジェネリクス があり、その主な目的は型安全なコンテナを提供することです。ただし、Javaのジェネリクスは具体化さ れず(コンパイル時のみ動作)、実行時には型消去されます。C++にはコンパイル時テンプレート があり、ジェネリックプログラミングとメタプログラミングをより広範にサポートします。C++テンプレートは、使用される各型に対して特殊化をインスタンス化します。 JavaとC++はどちらもアノテーション を提供しています。Javaのアノテーションを 使用すると、クラスに任意のカスタムメタデータを追加したり、アノテーション処理ツール を介してメタプログラミングを行うことができます。C++は、コンパイラ定義のメタデータを示す属性に加え、(C++26以降)アノテーションも提供しています。アノテーションはユーザーが定義でき、コンパイル時に読み取ることができますが、Javaのように実行時に読み取ることはできません。 JavaとC++はどちらも、ネイティブ型(基本 型または組み込み 型とも呼ばれる)とユーザー定義型(複合 型とも呼ばれる)を区別します。Javaでは、ネイティブ型は値セマンティクスのみを持ち、複合型は参照セマンティクスのみを持ちます。C++では、すべての型が値セマンティクスを持ちますが、任意の型への参照を作成することができ、参照セマンティクスを介してオブジェクトを操作できます。 C++ は任意のクラスの多重継承 をサポートしています。Java では、クラスは 1 つのクラスからのみ派生できますが 、クラスは複数のインターフェースを実装できます (言い換えれば、型の多重継承はサポートしていますが、実装の単一継承のみをサポートしています)。 Javaでは、インターフェースとクラスが明確に区別されています。C++では、多重継承と純粋仮想関数を用いることで、Javaのインターフェースとほぼ同じように機能するクラスを定義することが可能です。ただし、いくつかの小さな違いがあります。 Java は、言語と標準ライブラリの両方でマルチスレッド をサポートしています。Javasynchronizedのキーワードは、 マルチスレッドアプリケーションをサポートするミューテックスロック を提供します。 Java は、より高度なマルチスレッド同期のためのライブラリも提供しています。C ++11 では、C++ でのマルチスレッド用に定義されたメモリモデルがあり、スレッドの作成や多くの同期プリミティブのためのライブラリサポートがあります。C++ のメンバ関数は仮想関数 として宣言できます。これは、呼び出されるメソッドがオブジェクトの実行時型によって決定されることを意味します (動的ディスパッチとも呼ばれます)。デフォルトでは、C++ のメソッドは仮想ではありません (つまり、オプトイン仮想 )。Java では、メソッドはデフォルトで仮想ですが、キーワードを使用することで非仮想にすることができますfinal (つまり、オプトアウト仮想 )。overrideC++には、仮想メソッドのオーバーライドを示す言語レベルのキーワードがあります。Javaにはそのようなキーワードはありませんが、@Override オーバーライドを示すための注釈があり、既存のメソッドがオーバーライドされていない場合はコンパイラエラーが発生します。C および C++ の列挙型はプリミティブ型です。C の列挙型 ( enum) は暗黙的に整数型に変換されます (ただし、整数型からの変換はできません)。一方、C++ の列挙型 ( enum class) は厳密に型付けされており、暗黙的な変換は行われません。Java の列挙型はクラスのように動作し、コンストラクタやメソッドを定義できます。 C++ の単項演算子++と--: は「オペランドは変更可能なlvalue でなければならない。[省略] 結果は更新されたオペランドであり、lvalue である...」[ 21 ] ですが、Java では「上記のバイナリ数値昇格には、アンボックス化変換と値セット変換が含まれる場合があります。必要に応じて、合計が変数に格納される前に、値セット変換 {および/または [...] ボックス化変換} が適用されます。」[ 22 ] つまり、Java では初期化後に新しいオブジェクトを割り当てることで参照が変更されますが、C++ ではオブジェクトは依然として同じです。Integer i = 2 ; ++ i ; i Java では、ラムダ は「多項式」であり、その型はコンテキストに依存します[ 23 ] 。一方、C++ では、ラムダは実際の実行時クラスを持ち、それを使用してアクセスできdecltype 、言語モデルの一部です。Java のラムダは、割り当てられた関数インターフェースに依存します。
リソース管理 Javaは自動ガベージコレクションを提供しますが、 リアルタイムJava 仕様によって特定の状況下ではこれをバイパスできます。C++のメモリ管理は通常、コンストラクタ、デストラクタ、およびスマートポインタ によって行われます。C++標準ではガベージコレクションが許可されていますが、必須ではありません。ガベージコレクションは実際にはほとんど使用されていません。 C++では任意のメモリブロックを割り当てることができます。Javaではオブジェクトのインスタンス化によってのみメモリを割り当てます。Javaでは、任意のメモリブロックをバイト配列として割り当てることができます。 JavaとC++は、リソース管理に異なるイディオムを使用しています。Javaは主にメモリを解放できるガベージコレクションに依存していますが[ 7 ] 、C++は主にリソース取得は初期化である (RAII)イディオムに依存しています。これは、2つの言語間のいくつかの違いに反映されています。 C++では、複合型のオブジェクトをローカルなスタック変数として割り当て、スコープ外になると破棄するのが一般的です。Javaでは、複合型は常にヒープに割り当てられ、ガベージコレクタによって回収されます(ただし、エスケープ解析 を使用してヒープ割り当てをスタック割り当てに変換する仮想マシンは除きます)。 C++ にはデストラクタ[ 7 ] があり、 Java にはファイナライザ [ 7 ] があります。どちらもオブジェクトの解放前に呼び出されますが、大きく異なります。C++ オブジェクトのデストラクタは、オブジェクトを解放するために暗黙的に (スタックにバインドされた変数の場合) または明示的に呼び出される必要があります。デストラクタは、プログラム内でオブジェクトが解放される直前に同期的に 実行されます。したがって、C++ での同期的かつ協調的な初期化解除と解放は、RAII イディオムを満たします。C++ のデストラクタは、オブジェクトに関連付けられたリソースを取り戻す通常の方法であり、コンストラクタの必要な対義語です。[ 7 ] Java では、オブジェクトの解放はガベージ コレクタによって暗黙的に処理されます。Java オブジェクトのファイナライザは、最後にアクセスされてから解放される前に、非同期的に 呼び出されます。ファイナライザを必要とするオブジェクトはごくわずかです。ファイナライザは、解放前にオブジェクト状態のクリーンアップを保証する必要があるオブジェクト、通常は JVM の外部のリソースを解放するオブジェクトにのみ必要です。[ 7 ] ファイナライザを直接使用することは、予測不可能で、通常は危険であり、ほとんどの場合不要であるため、通常は推奨されません。[ 7 ] ファイナライザを C++ デストラクタのように考えないように注意する必要があります。[ 7 ] むしろ、try-with-resources またはtry-finallyブロックの方が、デストラクタと似た目的を達成します。[ 7 ] ファイナライザまたはクリーナーの問題の 1 つは、すぐに実行されることが保証されていないことです。[ 7 ] したがって、ファイナライザは、時間的に重要なタスクには決して使用しないでください。[ 7 ] さらに、ファイナライザは深刻なパフォーマンス ペナルティを伴い、オブジェクトの解放にかかる時間を大幅に増加させるため、Java 9 ではその使用は推奨されず、非推奨となっています。 C++ の RAII では、通常、1 種類のリソースが小さなクラスでラップされ、構築時にリソースを割り当て、破棄時にリソースを解放し、その間のリソースへのアクセスを提供します。このような RAII オブジェクトのみを含むクラスは、デストラクタを定義する必要はありません。なぜなら、このクラスのオブジェクトが破棄されると、RAII オブジェクトのデストラクタが自動的に呼び出されるからです。Java では、try- catch-finally構文を使用して、リソースの安全な同期解放を決定論的に実行できます。あるいは、 Java 7 で導入された -with-resources 構文を-構文tryよりも優先して使用する必要があります。 try-with-resources 構文は、より簡潔で読みやすいです。また、抑制された例外は破棄されず、抑制されたことを示す情報とともにスタック トレースに出力されるため、より役立つ診断情報も提供されます。tryfinally C++では、既に解放されたオブジェクトへの無効な参照である「 ダングリングポインタ」が 存在する可能性があります。ダングリングポインタを使用しようとすると、通常はプログラムが失敗します。Javaでは、ガベージコレクタは参照されているオブジェクトを破棄しません。 C++では、初期化されていないプリミティブオブジェクトが存在する可能性がある。Javaでは、デフォルト初期化が強制される。 C++ では、有効な参照がない割り当て済みオブジェクトが存在する可能性があります。このような到達不能なオブジェクトは 破棄 (解放) できず、メモリ リーク が発生します。一方、Java では、オブジェクトは(ユーザー プログラムから) 到達不能になるまでガベージ コレクタによって解放されません。 ( 弱い参照 がサポートされており、Java ガベージ コレクタと連携して、到達可能性の強度 を様々に調整できます。) Java のガベージ コレクションは多くのメモリ リークを防止しますが、状況によってはリークが発生する可能性は依然としてあります。[ 25 ] [ 26 ] [ 27 ] 自動ガベージ コレクタにより、Java ではメモリ管理について考える必要がないという誤った印象を与えるかもしれません。しかし、これは必ずしも正しくありません。大まかに言えば、これはプログラムが「メモリ リーク」、より正式には「意図しないオブジェクト保持」を持つ可能性があるためです。メモリリークが発生する例として、古い参照を削除しなかったことを除いて論理エラーのないプログラムが挙げられます。これにより、ガベージコレクタのアクティビティの使用が増加し、メモリ使用量 が増加します。極端な状況では、この問題により が発生することがありますjava.lang.OutOfMemoryErrorが、これはまれです。 この解決策は、オブジェクト参照を null にすることです。メモリ リークの 2 つ目の一般的な原因は、もはや関連性がなくなったキャッシュの使用です。古いキャッシュの使用によるメモリ リークの解決策は、キャッシュを を使って表現することですjava.util.WeakHashMap<K, V>。
ランタイム C++ は表現力に制約がないため、低レベルの言語機能 (配列へのアクセスチェックなし、生ポインタ、型変換など ) はコンパイル時に確実にチェックすることも、実行時にオーバーヘッドなしでチェックすることもできません。関連するプログラミングエラーは、低レベルのバッファオーバーフロー やセグメンテーション違反 につながる可能性があります。標準テンプレートライブラリ(STL) は、このようなエラーを回避するために、高レベルの RAII 抽象化 (vector、list、map など) を提供します。Java では、低レベルのエラーは発生しないか、 Java 仮想マシン (JVM)によって検出され、例外 の形式でアプリケーションに報告されます。 Java ランタイムでは例外が密接に結合しており、言語レベルの操作 (ゼロ除算、ヌル参照アクセス、無効なキャスト、範囲外配列アクセスなど) は例外をスローする可能性がありますが、C++ では通常、オブジェクトのみが例外をスローします (例外をスローする可能性がある[ 28 ] 、例外をtypeid()スローする可能性があるまたは[ 29 ] 、例外をスローする可能性がある、例外をスローする可能性がある[ 30 ] を除く)。さらに、C++ では例外を「無効」にすることができます ( GCC やClang [ 31 ] の、またはMSVC のなど)。これにより、コンパイラが例外処理用のコードを生成しなくなり、ステートメントでプログラムを で終了します。std::bad_typeidnewstd::bad_allocstd::bad_array_new_lengthnewstd::bad_allocdynamic_caststd::bad_cast-no-exceptions/Ehscstd::terminate()throw Javaにはチェック例外という機能 があり、これはブロック内で処理されなければならない例外でcatch、明示的に宣言された例外のみが関数を通して伝播できますthrows。例外を拡張しない例外はすべてjava.lang.RuntimeExceptionチェック例外とみなされます。一方、C++には「チェック例外」という概念はありませんが、関数は指定子をnoexcept使用して例外をスローしないように指定できます。C++17までは、C++はthrow関数シグネチャの例外指定によって動的な例外指定を許可しており、これはJavaのthrows例外指定と同様に、関数がスローできる型を指定できるようにしていました。[ 32 ] java.lang.ThrowableJava では、例外ステートメントによってスローされるのは、拡張クラスのみが許可されますthrow。C++ では、プリミティブ型を含むあらゆるオブジェクトがスローされ、キャッチされる可能性があります。Java では、java.lang.Error(キャッチするのが不適切と考えられる実行時エラーを表す) とjava.lang.Exception(キャッチするのが適切と考えられる回復可能なエラーを表す) を区別しますが、C++ では、通常の基本例外クラスは ですstd::exception。 Java 言語では、配列の範囲外アクセスの場合に特定の動作が要求され、一般的に配列アクセスの境界チェック が要求されます。これにより不安定性の原因となる可能性のあるものが排除されますが、通常は実行速度の低下という代償を伴います。場合によっては、特に Java 7 以降では、コンパイラ分析によって 境界チェックが不要であることが証明され、それが削除されることがあります。C++ ではネイティブ配列の範囲外アクセスに対する必須の動作がないため、ネイティブ配列の境界チェックは不要です。std::vector<T>ただし、C++ 標準ライブラリのコレクションなどでは、オプションの境界チェックが提供されます。要約すると、Java 配列 ( T[]) は「通常は安全、わずかに制約あり、オーバーヘッドが多い」のに対し、C++ ネイティブ配列は「オプションのオーバーヘッドあり、わずかに制約なし、安全でない可能性がある」です。Java コレクションなどでは、java.util.ArrayList<T>などの例外がスローされることがありますjava.lang.IndexOutOfBoundsException。C++26 では、標準ライブラリに「境界強化」が追加され、特定の未定義動作 (範囲外アクセスを含む) が契約違反として定義されるようになりました。[ 33 ] [ 34 ]
テンプレートとジェネリックの比較 C++とJavaはどちらも、それぞれテンプレート とジェネリクスという 汎用プログラミング のための機能を提供しています。両者は似たような問題を解決するために開発され、構文も似ていますが、実際にはかなり異なっています。
その他 JavaとC++は、コードを複数のソースファイルに分割する方法が異なります。 「モジュール 」という用語は、異なるものを指します。Javaでは、モジュール は複数のパッケージをグループ化するために使用されますが、C++では、モジュールとして宣言された 単一の翻訳単位 を表します。 importC++ では、コンパイル時に BMI (ビルド済みモジュールインターフェース) をロードすることでモジュールをインポートしますが、C++ では、モジュールはシンボルが属する名前空間を規定しません。一方、importJava では、クラスをエイリアスして完全修飾を避けるため (C++ の に相当)、コンパイル時に定義を「インポート」しません。これは、すべてのクラスが実行時に必要に応じてJava クラスローダー usingによって処理され、完全修飾することで ステートメントなしでも使用できるためです。importJavaソースファイルは、宣言するパブリッククラスの名前空間と一致している必要があり(パブリッククラスがない場合は任意の名前を使用できます)、また、そのファイルが属するパッケージは、そのファイルのパスと一致している必要があります。パッケージは、最大で1つのパブリッククラスしか宣言できません(ただし、複数の非パブリッククラスを持つことができます)。 C++ソースファイル(ヘッダーファイルまたはモジュールファイル)は、任意の名前を持つことができ、プログラマーが望む数のクラスを含めることができます。モジュールは、その場所のパスと一致する必要はありません。 コンパイルされたJavaクラスファイルは 、一般的にC++のオブジェクトファイル よりも小さくなります。これは、 Javaバイトコードが通常ネイティブ マシンコード よりもコンパクトであり 、Javaは静的リンクを行わないためです。 C++のコンパイルにはテキストの前処理 フェーズが追加されているが、Javaにはない。そのため、条件付きコンパイルをより適切にサポートするために、ビルドプロセスに前処理フェーズを追加するユーザーもいる。 Java の除算演算子と剰余演算子は、ゼロに切り捨てるように明確に定義されています。C ++11 より前は、C++ ではこれらの演算子がゼロに切り捨てるか「負の無限大に切り捨てる」かは規定されていませんでした。Javaと C++11 では-3/2常に です-1が、C++03 コンパイラはプラットフォームに応じて または を返す可能性があります。C99-1は除算を Java および C++11 と同じように定義しています。どちらの言語も (a と b が整数型の場合)すべてのおよび(ゼロ以外)に対してであることを保証します。C ++03 バージョンはプロセッサに固有の切り捨てモードを選択できるため、場合によってはより高速になります。-2(a / b) * b + (a % b) == aabb Javaでは整数型のサイズは固定されています(例えば、int32ビット整数は、long64ビット整数は)が、C++では整数とポインタのサイズは、与えられた制約内でコンパイラとアプリケーションバイナリインターフェース (ABI)に依存します。そのため、Javaプログラムはプラットフォーム間で一貫した動作を示しますが、C++プログラムは一部のプラットフォームに合わせて調整が必要になる場合があります。ただし、ローカルプラットフォームに適したより自然な整数サイズを使用することで、より高速に実行できる可能性があります。しかし、C/C++にも固定幅のサイズがあります(例えば、int32_t符号付き32ビット整数は、uint64_t符号なし64ビット整数はなど)。 C++とJavaを比較した例はWikibooks に掲載されています。
コンパイル済みのJavaプログラムを実行するだけでなく、Javaアプリケーションを実行するコンピュータは通常、Java仮想マシン (JVM)も実行する必要がありますが、コンパイル済みのC++プログラムは外部アプリケーションなしで実行できます。初期のJavaは、C++などの静的コンパイル言語に比べてパフォーマンスが大幅に劣っていました。これは、これら2つの密接に関連する言語のプログラムステートメントが、C++では少数の機械語命令にコンパイルされるのに対し、JVMで解釈されるとそれぞれ複数の機械語命令を含む複数のバイトコードにコンパイルされるためです。例:
パフォーマンス最適化は非常に複雑な問題であるため、C++とJavaのパフォーマンスの違いを一般的に定量化することは非常に困難であり、ほとんどのベンチマークは信頼性が低く偏りがあります。言語の性質が大きく異なるため、明確な質的差異を導き出すことも困難です。要するに、Javaは柔軟な高レベル抽象化に大きく依存しているため、本質的に非効率であり、最適化には厳しい限界があります。しかし、強力なJITコンパイラ(最新のJVM実装のように)を使用することで、いくつかの問題を軽減できます。いずれにせよ、Javaの非効率性が大きすぎる場合は、JNIを介してJavaからコンパイル済みのCまたはC++コードを呼び出すことができます。
Java言語に内在する非効率性には、主に以下のようなものがある。
すべてのオブジェクトはヒープに割り当てられます。スタック割り当てと同様のパフォーマンスを発揮する「バンプ割り当て」を使用する最新のJVMでは割り当てが非常に高速ですが、ガベージコレクタの呼び出しによりパフォーマンスが悪影響を受ける可能性があります。Oracle JDK 6以降、最新のJITコンパイラは、エスケープ解析またはエスケープ検出によって一部のオブジェクトをスタックに割り当てることで、この問題をある程度軽減しています。 効率的なデータベースシステムやメッセージングライブラリなど、パフォーマンスが重要なプロジェクトでは、手動のリソース管理にアクセスしたり、スタック割り当てを実行したりするために、内部の非公式APIを使用する必要がありましたsun.misc.Unsafe 。これは実質的に擬似ポインタを操作することになります。 標準コンテナを使用した場合でも、実行時に多くの型変換が必要になるため、パフォーマンスが低下します。しかし、これらの型変換のほとんどは、JITコンパイラによって静的に削除されます。 安全性の保証には実行時コストが伴います。例えば、コンパイラはコード内に適切な範囲チェックを組み込む必要があります。配列へのアクセスごとに範囲チェックを行うのは効率的ではないため、ほとんどのJITコンパイラは静的に範囲チェックを削除したり、内部ループの外に移動したりすることで、範囲チェックを排除しようとします(ただし、C++のネイティブコンパイラのほとんどは、範囲チェックがオプションで使用される場合に同様の処理を行います)。 低レベルの詳細にアクセスできないため、コンパイラが改善できないプログラムを開発者が改善することができない。[ 38 ] Java では、すべてのユーザー定義型に参照セマンティクスを強制的に使用させると、( JIT コンパイラによって省略されない限り)大量の不要なメモリ間接参照(またはジャンプ)が発生し、頻繁なキャッシュミス(いわゆるキャッシュスラッシング )につながる可能性があります。さらに、キャッシュを考慮した、あるいは考慮しない データ構造やアルゴリズムを用いたキャッシュ最適化は、多くの場合、パフォーマンスを桁違いに向上させるとともに、多くのキャッシュ最適化アルゴリズムに特徴的な時間計算量の縮退を回避するため、最も重要な最適化手法の一つとなっています。しかし、Java で義務付けられている参照セマンティクスでは、このような最適化を(プログラマも JIT コンパイラも)実際には実現することができません。 ガベージコレクション [ 39 ]は 、この形式の自動メモリ管理がメモリオーバーヘッドを導入するためである[ 40 ] 。 しかし、Javaの設計には多くの利点があり、その中には既に実現されているものもあれば、理論上のものに過ぎないものもある。
Java のガベージ コレクションは、 メモリ割り当てに通常使用される/よりも優れたキャッシュ コヒーレンスを持つ可能性があります。しかし、どちらのアロケータもヒープを均等に断片化し、どちらも優れたキャッシュ局所性を示すわけではないという議論があります。ただし、C++ では、ヒープ上に単一オブジェクトを割り当てることはまれであり、大量の単一オブジェクトは通常、STL コンテナや小さなオブジェクト アロケータを介してブロック単位で割り当てられます。[ 41 ] [ 42 ] std::malloc new 実行時コンパイルは、コードが実行されているプラットフォームに関する情報を使用して、コードをより効果的に改善できる可能性があります。ただし、最新のネイティブコンパイラ(C、C++など)のほとんどは、与えられたシステムの計算能力を最大限に活用するために、複数のコードパスを生成します。[ 43 ] また、ネイティブコンパイラは、マルチプラットフォームのJVMディストリビューションよりも、アーキテクチャ固有の最適化と命令セットをより効果的に活用できるという逆の議論も可能です。 実行時コンパイルでは、JIT コンパイラが仮想呼び出しのすべての可能なターゲットについてより多くの情報を持っているため、静的コンパイラよりも積極的な仮想関数のインライン化が可能になります。たとえそれらが異なる動的にロードされたモジュールにある場合でも同様です。現在利用可能な JVM 実装では、ほとんどの単相呼び出し、主に単相呼び出しと二相呼び出しのインライン化に問題はなく、Java 7 で追加された最近の invoke dynamic 拡張機能のおかげで、メガモルフィック呼び出しのインライン化も研究されています。[ 44 ] インライン化により、ループのベクトル化やループの展開 などのさらなる最適化が可能になり、全体的なパフォーマンスが大幅に向上します。 Javaでは、スレッド同期が言語に組み込まれているため、JITコンパイラはエスケープ解析によってロックを省略し[ 45 ] 、単純なマルチスレッドコードのパフォーマンスを大幅に向上させることができます。 また、C++ではいくつかのパフォーマンス上の問題が発生します。
ポインタが任意のアドレスを指すことを許可すると、ポインタエイリアシング の可能性により最適化が難しくなる場合があります。C++では、同じクラステンプレートのさまざまなインスタンスから生成されるコードは共有されないため(Javaの型消去ジェネリクスとは異なり)、テンプレートを過剰に使用すると、実行可能コードのサイズが大幅に増加する可能性があります(コード肥大化 )。しかし、関数テンプレートは積極的にインライン化されるため、コードサイズを削減できる場合があり、さらに重要なことに、コンパイラによるより積極的な静的解析とコード最適化が可能になり、多くの場合、テンプレートを使用しないコードよりも効率的になります。これに対し、Javaのジェネリクスは、ジェネリクスを使用しないコードよりも必然的に効率が劣ります。 従来のC++コンパイラでは、動的リンクはC++でのコード生成と最適化の後に行われるため、異なる動的モジュールにまたがる関数呼び出しはインライン化できません。しかし、MSVCやClang+LLVMといった最新のC++コンパイラは、リンク時コード生成オプションを提供しており、モジュールを中間フォーマットにコンパイルすることで、最終リンク段階でのインライン化が可能になります。
言語の公式標準および参照
言語仕様 C++言語は、ISO/IEC JTC1/SC22/WG21委員会によって発行されるISO規格であるISO/IEC 14882で定義されています。C ++ は通常 3 年周期でリリースされます。[ 46 ]
C++言語は、C++標準委員会と呼ばれる公開運営委員会を通じて進化しています。この委員会は、C++の生みの親であるビャーネ・ストロヴストルップ 氏、議長を務めるハーブ・サッター 氏、そして業界やユーザーグループの代表者(つまり利害関係者)を含む多くの著名人で構成されています。公開委員会であるため、誰でも自由に参加し、標準規格や技術仕様の今後のリリースに関する提案を行うことができます。委員会は現在、数年ごとに新しい標準規格をリリースすることを目指していますが、過去には厳格な審査プロセスと議論のため、新しい標準規格の公開間隔が長くなった時期もありました(1998年、2003年、2011年)。
Java言語は、Oracle社が発行する書籍であるJava言語仕様書[47]によって定義されています。Javaは 通常、 半年 に一度 のサイクルでリリースされますが、従来は2年に一度リリースされていました[ 48 ] [ 49 ]。
Java言語は、 Javaコミュニティプロセス と呼ばれるプロセスを通じて継続的に進化しており、世界のプログラミングコミュニティは、Javaコミュニティメンバー[ 50 ] と呼ばれる人々や組織のグループによって代表されています。このグループは、Java仕様要求と呼ばれる公開要求を送信することで、言語の強化に積極的に取り組んでおり、これらの要求は言語に統合される前に、正式な公開レビューに合格する必要があります。
Javaには確固たる標準規格が存在しないこと、そしてその仕様がやや不安定な性質を持っていることが、新たな言語機能やライブラリ機能の追加において、より安定性と保守性を求める関係者から常に批判の的となっている。対照的に、C++委員会は正反対の理由、すなわち厳格すぎ保守的すぎること、そして新バージョンのリリースに時間がかかりすぎることから、常に批判を受けている。
参考文献
引用文献 ↑ 「JDK 8 に符号なし整数演算 API が追加されました」。2017年 2 月 25 日のオリジナルからアーカイブ済み。2014年 3 月 17 日 取得 。 ↑ 「Javaチュートリアル:メソッドまたはコンストラクタへの情報の受け渡し」 。Oracle 。 2013年 2月17日 取得 。 ↑ 「Javaチュートリアル:スーパークラスとしてのオブジェクト」 。Oracle 。 2013年 2月17日 取得 。 。1 2 3 4 5 6 7 8 9 10 11 12 Bloch 2018 、pp. 29–33、第 2 章 項目 8: ファイナライザーとクリーナーを避ける。↑ 「XMPPソフトウェア » ライブラリ」 . xmpp.org . 2013年 6月13日 取得 。 ↑ Herb Sutter (2026年4月14日). "お知らせ: cppreference.com の更新" . isocpp.org . Standard C++ Foundation. ↑ Robert C. Martin (1997 年 1 月)。 「Java 対 C++: 批判的比較」 (PDF) 。2008 年 5 月 11 日に オリジナル (PDF)からアーカイブ済み。2007 年 12 月 15 日 に取得 。 ↑ 「参照型と参照値」 。Java 言語仕様書、第3版。 2010年 12月9日 取得 。 ↑ Horstmann, Cay; Cornell, Gary (2008). Core Java . Vol. I (第8 版). Sun Microsystems. pp. 140–141 . ISBN 978-0-13-235476-9 一部のプログラマー(そして残念ながら一部の書籍著者も)は、Javaプログラミング言語はオブジェクトに対して参照渡しを使用していると主張しています。しかし、それは誤りです。これはよくある誤解なので、反例を詳しく検討する価値があります... この議論は、Javaプログラミング言語がオブジェクトに対して参照渡しを使用していないことを示しています。代わりに、オブジェクト参照は値渡しされます 。 ↑デイテル 、 ポール、デイテル、ハーベイ (2009)。 『Java for Programmers 』。プレンティス・ホール。p. 223。ISBN 978-0-13-700129-3 他の言語とは異なり、Javaではプログラマーが値渡しか参照渡しかを選択することはできません。すべての引数は値渡しされます。メソッド呼び出しでは、プリミティブ値のコピー(int型やdouble型など)とオブジェクトへの参照のコピー(配列への参照を含む)の2種類の値をメソッドに渡すことができます。オブジェクト自体をメソッドに渡すことはできません。 ↑ 「GCCにおける浮動小数点演算のセマンティクス」 。GNU Foundation 。 2013年 4月20日 取得 。 ↑ 「Microsoft C++ コンパイラ、/fp (浮動小数点動作の指定)」 。Microsoft Corporation。2013 年 3 月 19 日 に取得 。 ↑ 「Java言語仕様4.3.1:オブジェクト」 。サン・マイクロシステムズ。 2010年 12月9日 取得 。 ↑ プログラミング言語 C++ '11 の標準、5.3.2 インクリメントとデクリメント [expr.pre.incr]。 ↑ Java™ 言語仕様、Java SE 7 版、第 15.14.2 章、15.14.3 章、15.15.1 章、15.15.2 章、 http://docs.oracle.com/javase/specs/ ↑ Oracle Corporation. 「第 15 章 式」 . docs.oracle.com . Oracle Corporation . 2026 年 5 月 31 日 取得 。 ↑ Satish Chandra Gupta; Rajeev Palanki (2005年8月16日)。 「Javaのメモリリーク - 捕まえられるものなら捕まえてみろ」 。IBM DeveloperWorks。 2012年7月22日の オリジナルからアーカイブ済み。 2015年 4月2日 取得 。 ↑ Javaにおけるメモリリークの修正方法(Veljko Krunic著、2009年3月10日) ↑ Javaでメモリリークを作成する( stackoverflow.com ) ↑ cppreference.com. "typeid operator" . cppreference.com . cppreference.com . 2026年 4月13日 取得 . ↑ cppreference.com. "新しい式" . cppreference.com . cppreference.com . 2026年 4月13日 取得 . ↑ cppreference.com. "dynamic_cast変換" . cppreference.com . cppreference.com . 2026年 4月13日 取得 . ↑ GNU Compiler Collection. "Exceptions: Chapter 3. Using" . gcc.gnu.org . GNU Compiler Collection . 2026年 4月13日 取得 . ↑ cppreference.com. "動的例外指定 (C++17 まで)" . cppreference.com . cppreference.com . 2026 年 4 月 13 日 取得 . ↑ サッター、ハーブ。 「ティアマトの訓練、クトゥルフの召喚解除:C++でUBモンスターを飼いならす」 。 サッターズミル 。 ↑ Konstantin Varlamov、Louis Dionne (2025年2月14日)。 「標準ライブラリの強化」 。open -std.org。WG21 。 ↑ "型エイリアス、エイリアステンプレート" . cppreference.com . 2022年 10月4日 取得 . ↑ 「変数テンプレート」 . cppreference.com . 2022年 10月4日 取得 。 ↑ Javaのジェネリクスはチューリング完全である ↑ Clark, Nathan; Amir Hormati; Sami Yehia; Scott Mahlke (2007). "Liquid SIMD: 軽量動的マッピングを使用した SIMD ハードウェアの抽象化". Hpca'07 : 216–227 . ↑ Hundt, Robert (2011年4月27日). "C++/Java/Go/Scalaにおけるループ認識" (PDF) . スタンフォード、カリフォルニア州: Scala Days 2011. 2022年10月9日のオリジナルから アーカイブ (PDF) . 2012年 11月17日 取得 . JavaはGCコンポーネントが大きいが、コードのパフォーマンスは良好である。[...] パフォーマンスに関しては、C++が圧倒的に優れていることがわかった。[...] Java版は実装が最も簡単だったが、パフォーマンスの分析が最も困難だった。特にガベージコレクションに関する影響は複雑で、調整が非常に困難だった。318 kB ↑ Matthew Hertz、Emery D. Berger (2005)。 「ガベージ コレクションと明示的メモリ管理のパフォーマンスの定量化」 (PDF) 。OOPSLA 2005。2017 年 7 月 6 日の オリジナル (PDF)からアーカイブ。2015 年 3 月 15 日 取得 。 特に、ガベージ コレクションが必要とするメモリの 5 倍のメモリを持っている場合、実行時のパフォーマンスは明示的メモリ管理と同等か、わずかに上回ります。ただし、ガベージ コレクションのパフォーマンスは、より小さなヒープを使用しなければならない場合、大幅に低下します。メモリが 3 倍の場合、平均で 17% 遅くなり、メモリが 2 倍の場合、70% 遅くなります。 ↑ Alexandrescu, Andrei (2001). Addison-Wesley (編). Modern C++ Design: Generic Programming and Design Patterns Applied. Chapter 4. pp. 77–96 . ISBN 978-0-201-70431-0 。↑ 「Boost Poolライブラリ」 。Boost 。 2013年 4月19日 取得 。 ↑ 実行時性能チェックのためのIA-32アーキテクチャプロセッサのターゲット設定 ↑ 「インライン化の「問題」の解決」Dr. Cliff Click | Azul Systems: Blogs」 。 2011年9月7日に オリジナルからアーカイブ済み 。 2011年 9月23日 に取得。 ↑ Java開発者向けOracleテクノロジーネットワーク ↑ Herb Sutter (2019年7月13日)。 「FAQ草稿:C++標準はなぜ3年ごとにリリースされるのか?」 。 herbsutter.com。herbsutter.com 。 ↑ Java言語仕様 ↑ Reinhold, Mark (2017年9月6日). 「Javaをより速く前進させる」 . 2017年 9月16日 取得 。 ↑ 「6ヶ月間のJavaリリース列車に全員乗車せよ」 。theserverside.com。2017年9月12日。 2017年 9月16日 取得 。 ↑ Javaコミュニティプロセス(SM)プログラム - 参加 - JCPメンバー ↑ ビャルネ・ストロヴストルップの FAQ: C++ はあなたの所有物ですか? ↑ ZDNet: OracleがSunを買収。Javaも所有。 2010年4月10日にWayback Machine に アーカイブされました。
情報源 ブロッホ、ジョシュア(2018)。『効果的なJava:プログラミング言語ガイド』 (第3 版)。アディソン・ウェスリー。ISBN 978-0134685991 。 Goetz, Brian; Peierls, Tim; Bloch, Joshua; Bowbeer, Joseph; Holmes, David; Lea, Doug (2006). Java Concurrency in Practice . Addison Wesley. ISBN 0-321-34960-1 。
外部リンク C++とJavaの違い オブジェクト指向メモリ管理:Java vs. C++ 第2章:JavaとCの違い(デビッド・フラナガン著『Java in a Nutshell』より) JavaとC++のリソース管理比較- 例を含む包括的な論文 JavaとCのパフォーマンス比較…再び… - JavaとC/C++のパフォーマンスの違いに関する詳細な議論 Hyperpoly - JavaとC++の比較