21

C++ では、メンバー関数をオーバーライド可能にするには、'virtual' キーワードを明示的に指定する必要があります。これは、メンバー関数がオーバーライド可能になると、仮想テーブルと vpointer を作成するオーバーヘッドが発生するためです (したがって、すべてのメンバー関数は暗黙的にオーバーライド可能ではありません)。パフォーマンス上の理由)。

また、サブクラスが同じ名前とシグネチャを持つ別の実装を提供する場合、メンバー関数を非表示にすることもできます (オーバーライドされていない場合)。

C# でも同じ手法が使用されています。なぜ Java がこの動作から離れて、デフォルトですべてのメソッドをオーバーライド可能にし、「final」キーワードを明示的に使用したときにオーバーライド動作を無効にする機能を提供したのか疑問に思っています。

4

6 に答える 6

27

より適切な質問は、「なぜ C# には非仮想メソッドがあるのですか?」ということかもしれません。または、少なくとも、非仮想としてフラグを立てるオプションを備えたデフォルトで仮想ではないのはなぜですか?

C++ には、(Brian が非常にうまく指摘したように) 必要ない場合は料金を支払わないという考えがあります。問題は、あなたがそれを欲しがる場合、これは通常、鼻からお金を払うことを意味するということです. ほとんどの Java 実装では、多くの仮想呼び出し用に明示的に設計されています。vtable の実装は高速になる傾向があり、非仮想呼び出しよりもほとんど高価ではありません。つまり、非仮想関数の主な利点が失われます。さらに、JIT コンパイラは実行時に仮想関数をインライン化できます。そのため、効率上の理由から、実際に非仮想関数使用する理由はほとんどありません。

したがって、それは主に最小の驚きの原則に帰着します。これは、すべてのメソッドが同じように動作することを示しており、半分が仮想で半分が非仮想ではありません。このポリモーフィズムを実現するには、少なくともいくつかの仮想メソッドが必要なので、それらをすべて仮想にするのは理にかなっています。さらに、同じ署名を持つ 2 つのメソッドを持つことは、自分自身を撃つことを求めているだけです。

ポリモーフィズムは、オブジェクト自体がその動作を制御する必要があることも規定しています。その動作は、クライアントがそれを FooParent と考えるか FooChild と考えるかによって決定されるべきではありません。

編集:だから、私は自分の主張について呼ばれています。この次の段落は私の推測であり、事実の記述ではありません。

これらすべての興味深い副作用は、Java プログラマーがインターフェースを非常に頻繁に使用する傾向があることです。仮想メソッドの最適化により、インターフェイスのコストが本質的に存在しないようになるため、ArrayList の代わりに (たとえば) List を使用し、後で単純な 1 行の変更で LinkedList に切り替えることができます。追加のペナルティなし。

編集: また、いくつかのソースをポニーします。元の情報源ではありませんが、Sun が HotSpot の仕組みについて説明しています。

インライン化

V テーブル

于 2009-05-07T20:23:50.557 に答える
19

ここから撮影(#34)

すべての非静的メソッドは常に動的バインディングを使用するため、Java には virtual キーワードはありません。Java では、プログラマーは動的バインディングを使用するかどうかを決定する必要はありません。C++ に virtual が存在する理由は、パフォーマンスをチューニングするときに効率をわずかに向上させるためにそれをオフにしておくことができるようにするためです (別の言い方をすれば、「使用しない場合は料金を支払う必要はありません」)。 )、これはしばしば混乱と不愉快な驚きをもたらします。final キーワードは、効率を調整するためのある程度の自由度を提供します。これは、このメソッドをオーバーライドできないため、静的にバインドできることをコンパイラに伝えます (したがって、C++ の非仮想呼び出しと同等のものを使用してインライン化されます)。これらの最適化はコンパイラ次第です。

おそらく、少し円形です。

于 2009-05-07T19:58:46.310 に答える
3

Java と C++/C# の間のより良いアプローチは何かを質問しようとしている場合、それは別のスレッドで反対方向に既に議論されており、ネット上で利用可能な多くのリソース

C# がデフォルトでメソッドを非仮想として実装するのはなぜですか?

http://www.artima.com/intv/nonvirtual.html

@Override アノテーションが最近導入され、新しいコードで広く採用されていることから、「すべての Java メソッドが暗黙的にオーバーライド可能である理由」という質問に対する正確な答えが示されています。それは設計者がミスを犯したからです。(そして、彼らはすでにそれを修正しました)

おー !これには反対票を投じます。

于 2009-05-08T03:31:02.043 に答える
1

Java では、VM 全体のコストに比べて仮想メソッドのコストが低いと言えます。C++ では、アセンブリのような C のバックグラウンドと比較して、かなりのコストがかかります。C から C++ への移行の結果として、デフォルトですべてのメソッドがポインターを介して呼び出されるようにすることを決定する人は誰もいません。あまりにも大きな変化です。

于 2009-05-08T08:01:03.487 に答える
1

Java は、すべてがオブジェクトであり、すべてが仮想メソッドである、より動的な言語定義に近づこうとしています。また、デザイナーが C++ の欠陥と見なすあいまいさと理解しにくい構造を回避したいため、演算子のオーバーロードはありません。この場合、1 つのクラス階層に 2 つのパブリック メソッド シグネチャを持ち、型に応じて異なるメソッドを呼び出す機能はありません。それを参照する変数の。

C# は、サブクラスの安定性と、サブクラスが予測どおりに動作することを確認することに重点を置いています。C++ はパフォーマンスに関心があります。

3 つの異なる設計優先順位により、異なる選択が可能になります。

于 2009-05-07T20:39:04.403 に答える