C++ で動的ディスパッチが実際にどのように機能するのだろうか。私の質問を説明するために、いくつかの Java コードから始めます。
class A
{
public void op(int x, double y) { System.out.println("a"); }
public void op(double x, double y) { System.out.println("b"); }
}
class B extends A
{
public void op(int x, double y) { System.out.println("c"); }
public void op(int x, int y) { System.out.println("d"); }
}
class C extends B
{
public void op(int x, int y) { System.out.println("e"); }
}
public class Pol
{
public static void main(String[] args)
{
A a = new C();
B b = new C();
/* 1 */ a.op(2, 4);
/* 2 */ b.op(2.0, 4.0);
}
}
実際a.op(2, 4)
にコンパイラが
A
(a
は 型の変数として宣言されているためA
) どのメソッドが に最も近いかを調べますop(int, int)
。- メソッドを見つけることができませんが、
op(int, int)
メソッドを見つけますop(int, double)
(単一の自動キャストでint
->double
)、 - 次に、この署名を修正します。
実行中、JVM は次のことを行います。
op(int, double)
コンパイラによって Class に修正されたシグネチャを持つメソッドをC
探しますが、見つかりません。- C のスーパークラスの内部を調べます。つまり
B
、 - 最後にメソッドを見つけて
op(int, double)
呼び出します。
call にも同じ原則が適用され、b.op(2.0, 4.0)
"b" が出力されます。
ここで、C++ で同等のコードを考えてみましょう
#include <iostream>
class A
{
public:
virtual void op(int x, double y) { std::cout << "a" << std::endl; }
virtual void op(double x, double y) { std::cout << "b" << std::endl; }
};
class B : public A
{
public:
void op(int x, double y) { std::cout << "c" << std::endl; }
virtual void op(int x, int y) { std::cout << "d" << std::endl; }
};
class C : public B
{
public:
void op(int x, int y) { std::cout << "e" << std::endl; }
};
int main()
{
A *a = new C;
B *b = new C;
/* 1 */ a->op(2, 4);
/* 2 */ b->op(2.0, 4.0);
delete a;
delete b;
}
a->op(2, 4)
Java のように "c" を出力します。しかしb->op(2.0, 4.0)
、再び "c" が出力されてしまい、途方にくれてしまいます。
動的ディスパッチのためにコンパイル時および C++ での実行中に適用される規則は正確には何ですか? (各関数の前に記述すると、C++ コードと同じ動作になることに注意してくださいvirtual
。ここでは何も変わりません)