在派生类中声明为Virtual的函数无法执行,而是产生了编译错误,为什么?

时间:2018-11-25 13:06:40

标签: c++ inheritance virtual-functions vtable static-typing

我了解虚函数和vTable,因此请问这是一个琐碎的问题。...

基于人们所解释的vTable和vPtr的概念,我理解并期望下面的程序可以工作,但会出错。

问题陈述: 在下面的示例中,按照理解,类B应该有一个包含函数f2()和f3()的“ vTable”。是不是 (因为f2()从A类继承但被覆盖,并且f3在B类本身中是虚拟的)

但是当进行函数调用“ obj-> f3(); ”时,出现如下所示的错误,我仍然想知道为什么会这样吗?

错误: 在函数'int main()'中: 31:10:错误:“类A”没有名为“ f3”的成员

// Example program
#include <iostream>
#include <string>

using namespace std;     
class A
{
    public:
    void f1() {cout<<"A::f1"<<endl;}
    virtual void f2() {cout<<"A::f2"<<endl;}
};    

class B: public A
{
    public:
    void f1() {cout<<"B::f1"<<endl;}
    void f2() {cout<<"B::f2"<<endl;}
    virtual void f3() {cout<<"B::f3"<<endl;}
};


int main()
{
    A* obj = new B();
    obj->f1();      // Early Binding (EB)
    obj->f2();      // Late Binding (LB)
    obj->f3();      // Error (though was expecting LB since f3 is virtual)
}

错误在函数'int main()'中:31:10:错误:'class A'没有名为'f3'的成员

2 个答案:

答案 0 :(得分:3)

实现细节(vtable与否)无关紧要。在标准(以及因此是编译器)的眼中,您的代码无效,因为A没有方法f3()并且obj的类型为A*,仅此而已

答案 1 :(得分:0)

在尝试描述任何C ++指令的语义之前,您需要返回C ++的最基本概念(它们是C基本概念的复杂变体): C ++是具有静态特性的编译语言。

类似于C中的C ++,在编译时会查找并绑定名称:使用的任何名称都必须引用可见的声明。编译器只能找到由表达式指定的类中的名称。 (表达式是语法构造,它们存在于编译时。)

在静态类型语言中,表达式类型是静态的,并根据声明在每个函数内部进行计算。

C ++还通过虚函数支持动态多态。不会使语言动态输入;基本原理没有改变:每个表达式的类型都是通过根据其语法和可见声明(对象,函数等的可见名称)应用规则来确定的。

使用动态多态性多态对象的类型(对象在运行时存在),即构造的类型(构造函数类的名称) (用于构造对象的操作)。确定虚拟调用将调用哪个函数体。有时称为后期绑定。

在您的格式错误的代码中,后期绑定不是问题,它甚至不会编译,无法运行,并且在运行时不会在任何多态对象上调用任何函数:

obj->f3();      // Error (though was expecting LB since f3 is virtual)

obj的指针类型为obj->something时与(*obj).something相同;并且obj是定义为

的局部变量
A* obj = (...something not relevant for the argument...);

因此obj的类型为A*,而*obj的类型为A,其类型定义为:

class A
{
    public:
    void f1() {cout<<"A::f1"<<endl;}
    virtual void f2() {cout<<"A::f2"<<endl;}
};

在由表达式f3指定的类中没有可见的任何成员*obj的声明。 因此调用的格式不正确。在编译时无效,编译器拒绝了。

这是该不良表格的所有相关分析的全部。其余的与静态类型的语言无关。没有在 relevant 代码中命名的派生类(这只是obj的声明类型,以及确定在哪里进行名称查找的表达式*obj)无关紧要。这是静态类型的 essence

此外,使代码的有效性取决于未直接命名的其他类(这些类可能是某些表达式引用的运行时构造的对象的类型),即使在编译时也无法检测到简单的错字时间。