C++とJavaのデフォルトの仮想関数の動作の違い
はじめに
オブジェクト指向プログラミングにおいて、仮想関数(virtual function)はポリモーフィズムを実現するための重要な仕組みです。基底クラスのポインタや参照を通じてメソッドを呼び出す際、実際にどのクラスのメソッドが実行されるかは、そのメソッドが仮想かどうかによって決まります。
C++とJavaでは、この「仮想」に関するデフォルトの挙動が正反対です。本記事では、両言語の違いをサンプルコードとともにわかりやすく解説します。
C++:デフォルトでは非仮想(non-virtual)
C++では、メンバ関数はデフォルトで非仮想です。動的ディスパッチを行いたい場合は、virtualキーワードを明示的に付ける必要があります。
virtualを付けない場合、基底クラスの参照やポインタ経由で呼び出すと、オブジェクトの実際の型に関係なく、常に基底クラスの関数が実行されます(静的ディスパッチ)。
サンプルコード
#include <iostream>
using namespace std;
class B {
public:
void s() { // デフォルトでは非仮想。「virtual」を付けると「In Derived」と出力される
cout << "In Base \n";
}
};
class D : public B {
public:
void s() {
cout << "In Derived \n";
}
};
int main(void) {
D d; // クラスDのオブジェクト
B &b = d; // 基底クラスBの参照
b.s();
return 0;
}
実行結果
In Base
s()は非仮想関数のため、コンパイル時に参照の型(B)に基づいて呼び出しが決定され、基底クラスの関数が実行されます。派生クラスの関数を実行したい場合は、基底クラスの宣言にvirtualを追加します。
Java:デフォルトで仮想(virtual)
一方Javaでは、インスタンスメソッドはデフォルトで仮想です。特別なキーワードを付けなくても、実行時にオブジェクトの実際の型に基づいてメソッドが選択されます(動的ディスパッチ)。
非仮想にしたい場合は、finalキーワードを使ってオーバーライドを禁止します。
サンプルコード
class B {
public void s() {
System.out.println("In Base"); // デフォルトで仮想
}
}
class D extends B {
public void s() {
System.out.println("In Derived");
}
}
public class Main {
public static void main(String[] args) {
B b = new D(); // 実際のオブジェクトはD
b.s();
}
}
実行結果
In Derived
変数の型はBですが、実際のオブジェクトがDであるため、オーバーライドされた派生クラスのs()が呼び出されます。これがJavaにおける動的ディスパッチの基本的な動作です。
まとめ:両言語の比較
| 項目 | C++ | Java |
|---|---|---|
| デフォルトの挙動 | 非仮想(non-virtual) | 仮想(virtual) |
| 仮想にする方法 | virtualキーワードを付ける | 何もしない(デフォルト) |
| 非仮想にする方法 | 何もしない(デフォルト) | finalキーワードを付ける |
| 設計思想 | パフォーマンス優先 | 柔軟性・拡張性優先 |
C++はパフォーマンスを重視し、必要な場合のみ仮想関数のコスト(vtable参照などのオーバーヘッド)を払う設計になっています。一方Javaは、オブジェクト指向の多態性を自然に活かせるよう、メソッドをデフォルトで仮想としています。それぞれの言語でポリモーフィズムを扱う際には、この根本的な違いを理解しておくことが重要です。
-
【比較解説】C++とJavaでデフォルトの仮想動作はどう違う?
ポリモーフィズム(多態性)を実現する「仮想関数」の扱いは、C++とJavaで大きく異なります。この違いを理解することは、両言語を使いこなす上で非常に重要です。 C++の場合:デフォルトは非仮想 C++では、クラスのメンバー関数はデフォルトで非仮想(non-virtual)です。これは、動的ディスパッチのオーバーヘッドを避け、パフォーマンスを優先する設計思想によるものです。仮想関数として動作させたい場合は、virtualキーワードを明示的に指定する必要があります。 class Base { public: virtual void display_msg() { //
-
Javaの配列におけるデフォルト値とは?各データ型の初期値をサンプルコード付きで解説
Javaには、同じ型の要素を固定サイズで連続的に格納できるデータ構造「配列(array)」が用意されています。配列は複数のデータをまとめて管理するために使われますが、実質的には「同じ型の変数を集めたもの」と考えると理解しやすくなります。 ここで重要なのが、配列を生成した際に要素を明示的に初期化しなくても、JVMが自動的にデフォルト値を割り当ててくれるという仕様です。そのため、newで配列を作成した直後でも、未初期化の状態のまま安全に各要素へアクセスできます。 データ型ごとのデフォルト値一覧 主なデータ型のデフォルト値は以下の通りです。 boolean … false byte / shor