我想了解以下代码:
#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-&gt; f(3.14F)调用Derived中的函数f。我不是100%肯定为什么。我看了一下(http://en.cppreference.com/w/cpp/language/implicit_cast),上面写着:
浮点类型的prvalue可以转换为任何整数类型的prvalue。小数部分被截断,即,小数部分被丢弃。如果该值不适合目标类型,则行为未定义
对我来说,你不能这样做,因为浮点数不适合int。为什么允许隐式转换?
其次,即使我只接受上述内容就可以了,第二次调用b-&gt; f(3.14F)也没有意义。 b-&gt; f(3.14F)正在调用虚函数f,因此动态解析它以调用与b所指向的对象的动态类型相关联的f(),这是一个Derived对象。因为我们被允许将3.14F转换为int,因为第一个函数调用表明这是合法的,这(我的理解)应该再次调用Derived :: f(int)函数。然而它调用了Base类中的函数。那为什么会这样?
编辑:这是我如何理解并向自己解释的。
b是指向Base对象的指针,因此我们只能使用b来访问Base对象的成员,即使b确实指向某个Derived对象(这是标准的OO /继承内容)。
此规则的唯一例外是Base的成员函数声明为virtual。在这种情况下,派生对象可以覆盖此功能,通过使用完全相同的签名提供另一种实现。如果发生这种情况,那么即使我们碰巧通过指向Base对象的指针访问成员函数,也会在运行时调用此Derived实现。
现在,在上面的代码片段中,我们没有任何重写,因为B :: f和D :: f的签名是不同的(一个是浮点数,另一个是int)。因此,当我们调用b-&gt; f(3.14F)时,唯一考虑的函数是原始的B :: f,这就是所谓的。
答案 0 :(得分:11)
这两个函数具有不同的签名,因此f
中的derived
不会覆盖base
中的虚函数。仅仅因为int
和float
类型可以隐式转换,这里没有效果。
virtual void f(float) { cout << "Base::f(float)\n"; }
virtual void f(int) { cout << "Derived::f(int)\n"; }
使用C ++ 11中的新override
关键字可以看出发生了什么的线索,这对于减少这些错误非常有效。
virtual void f(int) override { cout << "Derived::f(int)\n"; }
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"; }
};
现在将一个float传递给成员函数f
更接近于在base中定义的函数并产生:
Base::f(float)
Base::f(float)
答案 1 :(得分:2)
考虑隐藏的简单方法如下 - 查看d-> f(3.14F)行;从示例:
答案 2 :(得分:1)
由于这两个函数的参数类型不同,Derived
类中的参数类型实际上并不覆盖Base
中的一个。{1}}。相反Derived::f
隐藏Base::f
(目前我没有标准,所以我不能引用这一章。)
这意味着当您调用d->f(3.14f)
时,编译器甚至不会考虑B::f
。它解决了对D::f
的调用。但是,当您调用b->f(3.14f)
时,编译器可以选择的唯一版本是B::f
,因为D::f
不会覆盖它。
您对If the value can not fit into the destination type, the behavior is undefined
的阅读是错误的。它说值不是类型。因此值3.0f确实适合int,但3e11不适合。在后一种情况下,行为是不确定的。引用的第一部分A prvalue of floating-point type can be converted to prvalue of any integer type.
解释了为什么d->f(3.14f)
被解析为D::f(int)
- float确实可以转换为整数类型。