10

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。ここでは何も変わりません)

4

4 に答える 4

1

コンパイラに指示すると、変換時に警告/エラーが発生します。gcc を使用すると、コンパイラの引数-Wconversion -Werrorによってコードがコンパイルされなくなります。ご指摘のとおり、精度が低下する可能性があります。

このコンパイラ オプションをオンにしなかった場合、コンパイラは b->op(double, double) への呼び出しを B::op(int, double) に解決してくれます。

これはコンパイル時の決定であり、ランタイム/ポリモーフィックの決定ではないことに注意してください。

"b" ポインタの実際の vtable には、実行時に使用できるメソッド op(int, int) がありますが、コンパイラはコンパイル時にこのメソッドを認識しません。b ポインターが B* 型であると想定することしかできません。

于 2013-09-20T10:22:16.437 に答える