JavaとC++の比較
出典: フリー百科事典『ウィキペディア(Wikipedia)』
この記事は プログラミング言語の比較 の一部である。 |
比較全般 |
基本文法 |
文字列演算 |
文字列関数 |
|
評価戦略 |
Hello world |
|
CとC++の互換性 |
CとPascalの比較 |
C++とJavaの比較 |
C#とJavaの比較 |
C#とVisual Basic .NETの比較 |
目次 |
[編集] 設計思想
C++とJava言語との違いは、それら言語の伝統から辿ることができる。
- C++はC言語、手続き型プログラミング言語に静的型付けオブジェクト指向プログラミング言語の特色を追加するために作られ、機械語の効率的な実行のために設計された。
- Javaは当初、組み込みシステム上でネットワークコンピューティングをサポートするために作られた。Javaは移植性があり、セキュアであり、マルチスレッド対応であり、分散であり、そしてC++よりもシンプルになるように設計された。Javaの文法はCプログラマに馴染みやすいものが選ばれたが、Cとの直接的な互換性は維持されていない。
C++とJavaは開発の目的が異なるため、両者の方針とトレードオフに違いが生じている。
-
C++ Java Cとの後方(下位)互換性 Java以外の言語との互換性はない プログラマを当てにする プログラマを守る 低レベル機能に触れる オブジェクトを通してのみメモリアクセス 簡潔な表現 明確な表現 明示的な型破壊を許可 型安全性 マルチパラダイム(手続き型、オブジェクト指向、関数型、総称型) オブジェクト指向、総称型 演算子多重定義 演算子の効果は不変 限られた範囲の標準ライブラリ (GUI、ネットワーク、マルチスレッドを含む)機能豊富で容易に使用できる標準ライブラリ
これらの方針の違いの主な要因は、C++がC言語との互換性を維持しようとしたためである。
[編集] 言語の特徴
[編集] 文法
- Java文法はシンプルなLALRパーサによって解析できる文脈自由文法である。C++の構文解析は、若干複雑である。例えば、
Foo<1>(3);
はFooが変数であれば、比較シーケンスであるが、Fooがクラスのテンプレート名であればオブジェクトを生成する。
- C++は名前空間レベルの定数、変数、関数を許可する。Javaでは、宣言はクラスやインタフェースの中に書かなければならない。
- C++の
const
は、「論理的に読み取り専用データである」ことを明示し、データ型にも適用される。Javaのfinal
は、「変数が再び割り当てられない」ことを示す。基本型にとっては(const int
とfinal int
など)これらは等価だが、複雑なクラスでは異なる:
-
C++ Java const Rectangle r;
final Rectangle r = new Rectangle();
r= anotherRectangle; //
誤りr = anotherRectangle; //
誤りr.x = 5; //
'誤り'。 r は Rectangle型の定数r.x = 5; //
'正しい', rは依然として同じ長方形を参照している。
- C++は
goto文
をサポートする。Javaはサポートしないが、ラベル付break文とラベル付continue文で、構造上ややgotoライクな機能を提供する。実際には、Javaは、コードを読みやすくするため、構造化制御フローを強要する。 - C++はJavaが持たないやや低レベルな機能を提供する。C++には、特有のメモリ記憶位置や低レベルオペレーティングシステムコンポーネントを書くために必要なタスクを操るのに役立つポインタがある。同様にして、多くのC++コンパイラはインラインアセンブラをサポートする。Javaでは、そのようなコードは全て外部ライブラリに配置し、Java Native Interfaceを通してアクセスしなければならない。そのため、呼び出しのたびに、大きなオーバーヘッドが発生する。
[編集] 意味論
- C++はCとの互換性を保つため組込型の暗黙な型変換をある程度許可しているが、大抵のコンパイラでは警告を出す。また、複合型の暗黙型変換も定義できる。一方、Javaでは暗黙な変換としてネイティブ型の精度が大きくなる型変換(拡張変換)のみを許可する。他の変換は文法的に明示的なキャストを要求する。
- この影響はJavaとC++双方にある条件式(
if
、while
とfor
の脱出条件)にも現れる。条件式の型はboooleanであることが期待されるが、intからbooleanへの精度が狭められる暗黙的型変換(縮小変換)が無いため、if (a = 5)
のようなコードは、Javaではコンパイルエラーになる。これは、if(a == 5)
のミスタイプに対する対策となるが、if (x)
のようなコードをC++からJavaへ移植する際には型変換を明示することになる分、冗長になってしまう。
- この影響はJavaとC++双方にある条件式(
- 関数の引数を渡すとき、C++は参照渡しと値渡し両方をサポートする。Cと同様、ポインタの値渡し引数と間接参照により、参照渡し引数をシミュレートできる。Javaではすべての引数は値渡しであるが、オブジェクト(非プリミティブ変数)の引数は参照になり、これは間接参照が言語に備わっていることを意味する。
- 一般的に、Javaに組み込まれている型は指定されたサイズと領域をもつ。一方、C++の型は最小限度が定められているものの、正確な大きさは定められておらず、環境によって異なる。また同じコンパイラでもバージョンが違えば型の大きさが異なることもあるし、コンパイラの設定で変更可能な場合もある。
- C++の浮動小数点数の丸め誤差と精度と演算はプラットフォームに依存する。Javaは異なるプラットフォームでの一貫した結果を保証する高精度浮動小数点モデルを提供しているが、通常は最適な浮動小数点演算性能を得るためにより大雑把な演算モードが使われる。
- C++ではポインタを使ってメモリアドレスを直接操作できる。Javaはメモリアドレスを直接操作できるポインタを持っていない(オブジェクトの参照と配列参照だけはポインタを持っているが、どちらもメモリアドレスの直接アクセスを許可しない)。C++ではポインタのポインタを構築できるが、Javaではオブジェクトにアクセスするときだけ参照する。
- どちらの言語も配列は固定の長さをもつ。Javaでは配列はファーストクラスオブジェクトであるが、C++では単なる要素の連続であり、最初の要素へのポインタと長さによって参照されることが多い。それは配列の一部分だけでも効率よく参照できることを意味するが、配列外アクセスをチェックすることは殆ど不可能である。Javaの配列は長さがあらかじめ決められ、配列の一部分への参照が厄介である。また、強制的に配列外アクセスをチェックされる。
- C++ではポインタは関数やメソッド(関数ポインタまたはファンクタ)を指すことができる。Javaではこれと同等のメカニズムはオブジェクトやインタフェース参照を使用する。
- C++はプログラマが演算子多重定義を行うことが可能である。Javaでは多重定義された演算子は加算と文字列連結が可能な"
+
" と "+=
" だけである。 - Javaはリフレクションや任意に新しいコードを動的ロードする機能をサポートする標準APIを持つ。
- Javaはジェネリック型を持つ。C++はテンプレートを持つ。
- JavaとC++はいずれも、ネイティブ型(これらも"基本型"または"組込"型として知られる)とユーザ定義型(これも"複合"型として知られる)を区別する。Javaでは、ネイティブ型は値としての意味しかなく、複合型は参照としての意味だけを持つ。C++では、すべての型が値としての意味を持つが、任意の型の参照を作ることが可能である。
- C++は任意のクラスの多重継承をサポートする。Javaは型の多重継承をサポートするが、実装は単一継承のみをサポートする。Javaでは、クラスは一つのクラスからのみ受け継ぐことができるが、クラスは複数のインタフェースを実装することができる。
- Javaはインタフェースとクラスをはっきりと区別する。C++では、Javaではインタフェースが行う機能を、多重継承と純粋仮想関数(Javaでいうところの抽象クラス)を使って、クラスを定義することで可能にする。
- Javaはマルチスレッドをサポートした標準ライブラリと言語を持つ。
synchronized
Java予約語はマルチスレッドアプリケーションをサポートするシンプルでセキュアな相互排他ロック(Mutex)を提供するが、synchronized セクションはLIFOオーダーで残されなければならない。一方、C++のライブラリではもっと柔軟なMutexロックメカニズムを提供しているが、一般に利用可能なマルチスレッド用のメモリモデルが定義されていない。
[編集] リソース管理
- Javaは自動ガベージコレクションを必要とする。C++のメモリ管理は普通、手動で行われるか、スマートポインタを通して行われる。C++標準に則った上でガーベッジコレクションを実装することも可能だが、標準に必要とされているわけではなく、ガーベッジコレクションは実際には滅多に使われない。またC++ではオブジェクトを再配置できないが、もし可能であれば、現在のガーベッジコレクタは、明示に解放するスタイルよりも全体としてのアプリケーションの空間と時間効率が改善される。
- C++は任意のメモリブロックを割り当てる。Javaはオブジェクトインスタンス化を通してのみメモリを割り当てる。(注、Javaでは、バイト配列を作ることで、プログラマが任意のブロック割り当てをシミュレートできる。もっとも、Javaの配列もオブジェクトである。)
- JavaとC++はリソース管理に異なる慣習がある。Javaは主にガベージコレクションに頼り、メモリの再生だけができ、他のリソース上では最近の場面になるかもしれない。だが、C++は主にRAII (Resource Acquisition Is Initialization)という慣習に頼る。これは二つの言語間の以下のような様々な違いに現れている。
- C++では、複合型も組込型同様スタックに割り当てられ、スコープから外れると共に破棄される。Javaでは複合型は常にヒープに割り当てられ、ガーベッジコレクタによって回収される(ヒープ領域をスタック領域に変換するエスケープ解析を使用する仮想マシンでは除く)。
- C++はデストラクタを持っているが、Javaはファイナライザを持っている。双方はオブジェクトの解放時に優先的に呼び出されるが、それらは重大性が異する。C++オブジェクトのデストラクタは暗黙(スタックに割り当てられた変数の場合)またはオブジェクトを解放するときに明示的に呼び出されなければならない。デストラクタはオブジェクトが解放された時点で同期的に実行される。オブジェクトの破棄処理と解放が同期しているため、RAIIイディオムが実現される。Javaでは、オブジェクト解放はガーベッジコレクタによって暗黙のうちに解放される。Javaオブジェクトのファイナライザは、最後にアクセスされた後と、実際に解放される前に、ときどき非同期に呼び出されるが、決して何も起こらないかも知れない。非常に僅かしかないオブジェクトはファイナライザを要求する。;ファイナライザは解放状態を優先するオブジェクトの多少のクリーンナップを保証しなければならないオブジェクトによって要求されるだけである。—だいたいは、JVM外のリソースへ放出される。Javaでは安全な同期によるリソース解放は、try/finally文を構築して明示的に行われなければならない。
- C++では、dangling pointer(破棄されたオブジェクトを参照するポインタ)を持つことが可能である。dangling pointerを間接参照するときは、大抵プログラムの失敗が結果として起こっているときである。Javaでは、ガーベッジコレクタは参照されているオブジェクトは解放しない。
- C++では初期化されていないプリミティブなオブジェクトを持つことが可能である。Javaはデフォルトの初期化を強制する。
- C++では、領域を割り当てられたが到達不能であるオブジェクトが発生してしまうことがある。到達不能オブジェクトとは、それへの到達可能な参照が全く存在しないオブジェクトのことである。到達不能オブジェクトは解放(破棄)することができず、メモリリークを引き起こす。それとは対照的に、Javaではオブジェクトは、それがユーザプログラムによって到達不可能になるまでにガーベッジコレクタによって解放される。(注: 異なる到達可能性の強さを考慮に入れた、Javaのガーベッジコレクタとともに働く、弱い参照がサポートされている。) Javaにあるガーベッジコレクションは多くのメモリリークを防ぐが、リークは、未だにいくらかの状況において発生しうる。[要出典]
- Javaは簡単に非メモリリソースを漏らすが、一方で慣用的なC++では漏らしにくい。
[編集] ライブラリ
- JavaはC++と比べ随分と巨大な標準ライブラリを持つ。C++標準ライブラリは文字列、コンテナ、I/Oストリームのような比較的一般的な目的のコンポーネントだけを提供する。
- Java標準ライブラリはネットワーキング、グラフィカルユーザインタフェース、XML処理、ロギング、データベースアクセス、暗号化やそのか様々な領域のコンポーネントを含む。この追加機能性はC++では(よくフリーで)サードパーティライブラリによって可能になるが、いくつかの実装では用意されていない。
- C++はたいていCとの後方互換性があり、(多くのオペレーティングシステムのAPIのような)CライブラリはC++から直接アクセスできる。Javaではよりリッチな標準ライブラリの機能性[要出典]が、大抵はプラットフォーム固有ライブラリにである多くの特徴があるものにクロスプラットフォームでアクセスできるものを提供する。
- Javaからネイティブなオペレーティングシステムやハードウェア機能に直接アクセスするには、Java Native Interfaceを使用する必要がある。
[編集] ランタイム
- C++は通常、機械語に直接コンパイルされてから、オペレーティングシステムによって直接実行される。Javaは通常バイトコードにコンパイルされてからJava仮想マシン (JVM)がインタプリタでバイトコードを解釈するか、またはJITがバイトコードをマシンコードにコンパイルしつつ実行される。理論上動的再コンパイルは特にJavaなど、どの言語でも使うことができるが、しかし現在は、両方の言語が動的に再コンパイルされることは稀である。
- 強制によらない表現力のため、C++の多くの特徴(e.g. チェックされない配列アクセス、未使用ポインタ、型ずれ)はコンパイル時または実行時の不適当なオーバーヘッド無しに信頼できるチェックを得られない。関係のあるプログラミングエラーは低レベルバッファオーバフロー、ページフォールト、セグメンテーションフォルトを導いてしまう。Standard Template Libraryがそのようなエラーを避けることを助ける高レベルな(ベクトル、リスト、マップのような)抽象概念を提供しているが、Javaではそのようなエラーは単純に起こすことも、JVMに検出されることも無く、例外によってアプリケーションに報告される。
- Java言語は、配列アクセスの境界チェックを一般的に要求する領域外配列アクセスが起きたとき、明確な振る舞いを要求する。これは通常、実行を遅らせる犠牲を払って不安定の源になる可能性があるものを除去する。いくつかのケースではコンパイラ解析が不必要な境界チェックを探し、それを消去する。C++はネイティブな配列の配列外アクセスの振る舞いを要求しない。したがって、ネイティブな配列の境界チェックを要求しない。ただしstd::vectorのようなC++標準ライブラリコレクションでは、随意で配列の境界チェックを提供している。要約すると、Javaの配列は「常に安全で、厳しく強いられる、可能な限り高速」だがC++のネイティブ配列は「常に高速、完全に強制されない、潜在的に危険」ということである。
[編集] その他
- JavaとC++は多くのソースファイルでコードを分割するために異なる技術を使用する。Javaはすべてのプログラム定義でファイル名とパスが影響するパッケージシステムを使用する。Javaでは、コンパイラは実行可能クラスファイルをインポートする。C++はソースファイル間で宣言を分割するヘッダファイルのソースコード包含システムを使用する。(参考 importとincludeの比較。)
- コンパイルされたJavaバイトコードファイルは通常、C++の機械語コードファイルよりも小さい。第一に、Javaバイトコードは通常、ネイティブな機械語よりもコンパクトである [要出典]。第二に、C++にあるテンプレートやマクロは標準ライブラリにそれらを含んでおり、コンパイルの後で結果的に類似コードの重複をもたらしてしまう。第三に標準ライブラリと共に、動的リンクはコンパイル時にライブラリとの結合を消去する。反面、Javaバイトコードを翻訳実行する環境(JVMやJIT)が要求される。
- C++コンパイラは、Javaにはない、言葉通りのプリプロセッシング(前処理)の段階があることが特徴的である。これを用いるために、Javaユーザの中には、条件付きコンパイルのより良いサポートのためにビルドプロセスにプリプロセッサを付加する者がいる。
- 双方の言語は、配列が固定サイズである。Javaでは、配列はファーストクラスオブジェクトであるが、C++では配列は配列のベースとなるオブジェクトの連続した領域であり、最初の要素と随意的な配列の長さをポインタを使って参照しているに過ぎない。Javaでは、配列は境界チェックされ、長さもわかっているが、C++では配列自体を連続した領域として扱うことができる。C++とJava双方は、リサイズ、サイズ保存できるコンテナクラス(それぞれ、std::vector と
java.util.Vector
またはjava.util.ArrayList
)を提供している。 - Javaの除算と剰余演算子は0を切り捨てるよう正しく定義されている。C++は、これらの演算子が0を切り捨てるか「マイナス無限大に切り捨てる」かを明確に指定しない。 Javaでは、 -3 / 2は常に-1となるだろう。しかし、C++コンパイラは プラットフォームに依存して、-1を返すかも知れないし-2を返すかも知れない。C99はJavaと同じ流儀で除算を定義している。双方の言語はすべてのaとb (b != 0)で
(a/b)*b + (a%b) == a
を保証する。C++はバージョンによってはときどき、プロセッサにネイティブであるどんな短縮モードもつみ取る高速なものになることがある。ただし、div関数を用いればC++でも常に-3 / 2の商として-1という結果を得られる。 - 整数型の大きさはJavaでは定義されている(intが32ビット、longが64ビット)。しかしC++での整数やポインタの大きさは一定の制約があるものの基本的にコンパイラ依存となっている。したがって、慎重に書かれたC++コードは32ビットプロセッサ上で絶えずきちんと機能を果たし、かつ、64ビットプロセッサの能力のアドバンテージを得ることができる。しかし、プロセッサの語長に対する注意を払わずに書かれたC++プログラムは、コンパイラによっては正常に実行できない実行可能ファイルを出力するかもしれない [要出典]。比べて、Javaの固定された整数のサイズはプログラマが異なる整数型のサイズを懸念する必要が無いことを意味し、プログラムは正確に走らせることができるだろう [要出典]。これは、逆に言うとJavaコードが任意のプロセッサの語長を使って実行させることができなくなって以来、パフォーマンスペナルティを被るかもしれないということでもある。
[編集] パフォーマンス
このセクションは、WindowsとLinuxのような共通OSでのC++とJava相互の演算パフォーマンスを比較する。
Javaの初期バージョンはC++のような言語によって静的コンパイルされたものによって著しく性能を追い抜かれていた。これは、同じ機能を実現したソースコードであっても、C++はハードウェアがネイティブに解釈できる機械語にコンパイルするのが一般的なのに対し、Javaでは様々なハードウェア上で共通の仮想機械語であるJavaバイトコードにコンパイルし、JVMによりインタープリト実行される。例として:
Java/C++構文 | C++が生成したコード | Java生成したコード |
---|---|---|
vector[i]++; | mov edx,[ebp+4h] mov eax,[ebp+1Ch] |
aload_1 iload_2 |
これは小さなフットプリント(情報収集)要求のため、依然として組み込みシステムのケースだが、
長期稼働するサーバやデスクトップのためにジャストインタイム(JIT) コンパイラテクノロジによってJavaは速度改善を図り、Javaプロセスはパフォーマンスギャップに緻密であり、様々なケースでは、Javaにパフォーマンスアドバンテージが与えられている。実際には、Javaバイトコードは、C++の静的コンパイルに似た方法で、C++に似た命令シーケンスを結果として出し、実行時にマシン語にコンパイルされる。
様々な結果を見せる計算ベンチマークの様々な研究は、以下の多数の理由として、JavaはC++よりも高速であると主張する:[1][2]
- ポインタは任意のデータやコードを参照しても良くなって以来、ポインタを最大限に利用することを難しくしている。
- 新たに確保されたメモリはガーベジコレクションがメモリと確保領域を凝集させため、共に親密になり、それゆえ、メモリ上で連続している領域となり、より'キャッシュ内'に存在しうるものとなる。
- 実行時コンパイルはそれがどのプロセッサの上で実行されるか、どのコードを実行するかが解っているため、よりベターなジョブをこなすことができる。 - ホットスポット
静的コンパイラにはコマンドラインオプションやプログラムを使用し、プロセッサと他の最適化オプションを配慮し指示を与えることができるが、これらの選択はどこでいつコードが実際に実行されるかを間違えるかも知れない。JITテクノロジはコンパイラ進化で次の段階へと目標を達成した。歴史上重要な測量をベースとした最適化を通しての指示命令から、現在の実行時測量を使用する最適化を通しての指示命令へと。
マイクロベンチマークの幅広い研究は、結果に非常に大きな変化を示す。一般的に、Javaは、メモリ確保やファイルI/Oのような演算においてはC++より性能が優れている徴候を示すが、C++は算術や三角法演算ではJavaよりも性能が優れている徴候を示す。 [3] 数値演算では、Javaは各々新バージョンでの重大な進歩を得たが、ある程度、すべてのプラットフォームに対応する結果、浮動小数点の再現性を要求するため、未だにC++やFortranより遅い。[4]
[編集] 参照
- ^ "Performance of Java versus C++" by J.P. Lewis and Ulrich Neuman, USC, Jan. 2003 (updated 2004)
- ^ "Java will be faster than C++" by Kirk Reinholtz, JPL, Apr 2001
- ^ "Microbenchmarking C++, C# and Java" by Thomas Bruckschlegel, Dr. Dobbs, June 17, 2005
- ^ "Java and Numerical Computing" by Ronald F. Boisvert, José Moreira, Michael Philippsen and Roldan Pozo, NIST, Dec 2000
[編集] 関連項目
[編集] 外部参考資料
- How Java Differs from C — excerpt from Java in a Nutshell by David Flanagan
- Java vs. C++ resource management comparison - Comprehensive paper with examples
- Why Java Will Always Be Slower than C++ - "Java is high performance. By high performance we mean adequate. By adequate we mean slow." - Mr. Bunny
カテゴリ: 出典を必要とする記事 | Java言語 | C++