次のコードを理解しようとしています。
#include<iostream>
using namespace std;
class Base {
public:
virtual void f(float) { cout << "Base::f(float)\n"; }
};
class Derived : public Base {
public:
virtual void f(int) { cout << "Derived::f(int)\n"; }
};
int main() {
Derived *d = new Derived();
Base *b = d;
d->f(3.14F);
b->f(3.14F);
}
これは印刷します
Derived::f(int)
Base::f(float)
正確な理由はわかりません。
最初の呼び出し d->f(3.14F) は、Derived で関数 f を呼び出します。理由は 100% わかりません。私はこれ(http://en.cppreference.com/w/cpp/language/implicit_cast)を見ました:
浮動小数点型の prvalue は、任意の整数型の prvalue に変換できます。小数部分は切り捨てられます。つまり、小数部分は破棄されます。値が宛先の型に収まらない場合、動作は未定義です
float は int に収まらないため、これを行うことはできません。この暗黙的な変換が許可されるのはなぜですか?
第二に、上記を問題ないと受け入れたとしても、b->f(3.14F) への 2 回目の呼び出しは意味がありません。b->f(3.14F) は仮想関数 f を呼び出しているため、これは動的に解決され、派生オブジェクトである b が指すオブジェクトの動的型に関連付けられた f() を呼び出します。3.14F を int に変換することが許可されているため、最初の関数呼び出しはこれが有効であることを示しているため、(私の理解では) Derived::f(int) 関数を再度呼び出す必要があります。それでも、Base クラスの関数を呼び出します。では、これはなぜですか?
編集:これが私がそれを理解し、自分自身に説明した方法です。
b は Base オブジェクトへのポインターであるため、b を使用して Base オブジェクトのメンバーにアクセスすることしかできません。たとえ b が実際に Derived オブジェクトを指していても (これは標準の OO/継承のものです)。
この規則の唯一の例外は、Base のメンバー関数が virtual として宣言されている場合です。このような場合、Derived オブジェクトはこの関数をオーバーライドし、まったく同じ署名を使用して別の実装を提供できます。これが発生すると、たまたま Base オブジェクトへのポインターを介してメンバー関数にアクセスしている場合でも、この Derived 実装が実行時に呼び出されます。
上記のコード スニペットでは、B::f と D::f のシグネチャが異なるため (一方が float、もう一方が int)、オーバーライドは行われません。したがって、b->f(3.14F) を呼び出すとき、考慮される唯一の関数は、呼び出された元の B::f です。