对虚函数和派生类的混淆

mic*_*ies 4 c++ virtual-functions name-hiding

我想了解下面的代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

这打印

Derived::f(int)
Base::f(float)
Run Code Online (Sandbox Code Playgroud)

我不确定为什么.

第一个调用d-> f(3.14F)调用Derived中的函数f.我不是100%肯定为什么.我看了一下(http://en.cppreference.com/w/cpp/language/implicit_cast),其中说:

浮点类型的prvalue可以转换为任何整数类型的prvalue.小数部分被截断,即,小数部分被丢弃.如果该值不适合目标类型,则行为未定义

对我来说,你不能这样做,因为浮点数不适合int.为什么允许这种隐式转换?

其次,即使我只接受上面的判断,第二次调用b-> f(3.14F)也没有意义.b-> f(3.14F)正在调用虚函数f,因此动态解析它以调用与b指向的对象的动态类型相关联的f(),这是一个Derived对象.因为我们被允许将3.14F转换为int,因为第一个函数调用表明这是合法的,这(我的理解)应该再次调用Derived :: f(int)函数.然而它调用了Base类中的函数.那么为什么呢?

编辑:这是我如何理解并向自己解释的.

b是指向Base对象的指针,因此我们只能使用b来访问Base对象的成员,即使b确实指向某个Derived对象(这是标准的OO /继承内容).

此规则的唯一例外是Base的成员函数声明为virtual.在这种情况下,Derived对象可以覆盖此函数,通过使用完全相同的签名提供另一个实现.如果发生这种情况,那么即使我们碰巧通过指向Base对象的指针访问成员函数,也会在运行时调用此Derived实现.

现在,在上面的代码片段中,我们没有任何重写,因为B :: f和D :: f的签名是不同的(一个是浮点数,另一个是int).因此,当我们调用b-> f(3.14F)时,唯一考虑的函数是原始的B :: f,这就是所谓的.

111*_*111 11

这两个功能有不同的特征,所以fderived不重写虚函数base.仅仅因为类型intfloat可以隐式转换在这里没有效果.

virtual void f(float) { cout << "Base::f(float)\n"; }
virtual void f(int) { cout << "Derived::f(int)\n"; }
Run Code Online (Sandbox Code Playgroud)

使用overrideC++ 11中的new 关键字可以看到正在发生的事情的线索,这在减少这些类型的错误方面非常有效.

virtual void f(int) override { cout << "Derived::f(int)\n"; }
Run Code Online (Sandbox Code Playgroud)

gcc从中产生错误:

virtual void Derived :: f(int)'标记覆盖,但不覆盖

错误:'f'标记为'覆盖'但不覆盖任何成员函数

http://en.cppreference.com/w/cpp/language/override

编辑:

对于第二点,您实际上可以公开float重载base,derived其中公开了一个隐式兼容的成员函数.像这样:

class Derived : public Base {
public:
    using Base::f;
    virtual void f(int) { cout << "Derived::f(int)\n"; }
};
Run Code Online (Sandbox Code Playgroud)

现在将float传递给member函数会f更接近base中定义的函数并生成:

Base::f(float)
Base::f(float)
Run Code Online (Sandbox Code Playgroud)