C++ 在运行时如何决定是调用派生类函数还是基类函数?
如何在内部(运行时)决定调用哪个函数,基类函数还是派生类函数。在下面的示例代码中,如何决定调用B的fn()和A的fn2()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(
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的流行模式可能是正确的。但是编译器可以自由选择他们想要的任何东西,直到虚拟功能的可观察行为没有改变为止。