C++ 在运行时如何决定是调用派生类函数还是基类函数?

C++ 在运行时如何决定是调用派生类函数还是基类函数?,c++,C++,如何在内部(运行时)决定调用哪个函数,基类函数还是派生类函数。在下面的示例代码中,如何决定调用B的fn()和A的fn2() Class A { virtual void fn() { std::cout << "A" < <std::endl; } virtual void fn2() { std::cout << "A-fn2" < <std::endl; } }; Class B : A { void fn(

如何在内部(运行时)决定调用哪个函数,基类函数还是派生类函数。在下面的示例代码中,如何决定调用B的fn()A的fn2()

Class A { 
    virtual void fn() { std::cout << "A" < <std::endl; }
    virtual void fn2() { std::cout << "A-fn2" < <std::endl; }
    };

Class B : A
{
    void fn() { std::cout << "B" < <std::endl; }
}

int main() {
    B b = new B;
    A *a = &b;
    a->fn();
    a->fn2();
}
A类{

virtual void fn(){std::cout更正编译错误后,您的问题的答案是:输出将是:
A-fn2

Class A { 
    virtual void fn() { std::cout << "A" < <std::endl; }
    virtual void fn2() { std::cout << "A-fn2" < <std::endl; }
    };

Class B : A
{
    void fn() { std::cout << "B" < <std::endl; }
}

int main() {
    B b = new B;
    A *a = &b;
    a->fn();
    a->fn2();
}
您的代码中没有需要任何运行时决策的
virtual
函数。编译器本身将评估对该函数的调用


假设将
A
的方法设置为虚拟的
,那么函数调用决策在运行时根据引用的对象进行。

您所拥有的被称为动态/运行时多态性
规则是:
根据指针指向的实际对象,在运行时选择要在运行时调用的方法。
编译器执行此操作的方式完全取决于实现。通常,您的代码应该只依赖于行为,而不依赖于其内部。但是,所有已知的编译器都通过虚拟表(
vtbl
)和虚拟指针(
vptr
)机制来实现此机制

如何实现运行时多态性?

一旦一个类有了一个
virtual
方法,该类就被称为多态类,编译器为该类创建一个
vtbl
vtbl
存储该类中所有虚拟方法的地址。编译器还向该类的每个对象添加一个特殊指针
vptr
指向
vtbl

一旦一个类从这样一个多态类派生,编译器就会用派生类中的溢出函数的地址替换派生类的
vtbl
中方法的地址。对于非溢出方法,地址仍然是基类方法的地址

因此,每个类通常有一个
vtbl
,而每个对象实例有一个
vptr
,它指向
vtbl
。每个类的
vtbl
存储其自己的虚拟方法的地址

在运行时,将获取
this
指针内的
vptr
,并进一步获取
vtbl
中的适当方法地址,然后调用它,此机制称为动态调度。
因此,由于这种机制,可以根据指针指向的对象的类型来确定要调用的适当方法

这个C++常见问题解答是一个很好的进一步阅读:

这不可能编译,对吧?另外,你可能想看看我忘记添加虚拟关键字了。对此表示歉意。你能详细解释一下决定是如何做出的吗?面试官特别想知道内部实现。他暗示了一个叫做虚拟表的东西。我忘记添加虚拟关键字了。Ap你能详细解释一下这个决定是如何做出的吗?面试官特别想知道这个决定的内在含义。他暗示了一种叫做虚拟的东西Tables@NiklasB.:虽然指出这取决于实现,但所有已知的编译器都通过虚拟表和指针机制实现动态调度,所以这个论点虽然正确,但实际上是无效的。@Als:这就是为什么我写了一个关于vtables的答案。干杯。@NiklasB.,你在答案中的建议对于vtables的流行模式可能是正确的。但是编译器可以自由选择他们想要的任何东西,直到虚拟功能的可观察行为没有改变为止。