C++ 继承中的虚拟调用

C++ 继承中的虚拟调用,c++,inheritance,C++,Inheritance,我如何知道对函数的调用是在编译时还是在运行时从任何类解析的 例如-在下面的例子中,当调用show()时,是否会在运行时解析派生类 #include <iostream> using std::ostream; class Base { public: virtual void show() { show(); //Call derived class 'show()' } }; class Derived : public Base { p

我如何知道对函数的调用是在编译时还是在运行时从任何类解析的

例如-在下面的例子中,当调用
show()
时,是否会在运行时解析派生类

#include <iostream>
using std::ostream;

class Base 
{
public:
    virtual void show() {

         show(); //Call derived class 'show()'
    }
};

class Derived : public Base {

public:

    void show() {

         show(); //Call to itself, Would this call be resolved at run-time?
    }
};

ostream& operator <<(ostream &os, Base &obj)
{

    obj.Base::show();
    return os;
}

int main()
{
    Derived D;

    cout << D << endl;
}
#包括
使用std::ostream;
阶级基础
{
公众:
虚拟虚空显示(){
show();//调用派生类'show()'
}
};
派生类:公共基{
公众:
无效显示(){
show();//调用自身,是否在运行时解析此调用?
}
};

ostream&operator每当您通过具体类型的对象(即没有指针)调用成员函数时,静态类型是已知的,因此编译器在编译时解析正确的函数。 在运行时解析虚拟函数的唯一时间是使用多态性在指向对象的指针上调用它们时

在您的示例中,对show()的调用是通过“this”指针进行的,随后将在运行时解析这些调用。考虑到继承链中总是存在一个类,它实现了Sun()/


显式限定的调用“obj.Base::show()”显然是在编译时解决的。

只要编译器能够确定要调用的函数重载,它就会解决。它保证在以下情况下能够做到这一点:

  • 它具有对象的完整类型(例如
    Foo-Foo;Foo.bar();
  • 告诉它调用哪个重载(例如,
    Foo-Foo;Foo.Bar::Bar();

但在不太明显的情况下,它可能能够做到这一点——即,如果它能够找出“指向
Foo
的指针实际上总是指向
Bar
”。这就是所谓的虚拟化,是优化编译器夜视仪的一部分。根据编译器和实际代码的不同,可能会执行此优化,并直接调用函数,而无需通过vtable。

为了回答您的问题,在代码中,obj.Base::show()是在编译时解析的,因为显式调用了基函数。如果您想在运行时解决这个问题,那么您可以使用一个指向基的指针,并向它传递一个指向派生对象的指针

例如:

ostream& operator <<(ostream &os, Base *obj)
{    
    obj->show();
    return os;
}

int main()
{
    Derived D;    
    cout << &D << endl;
}

ostream&operator只是用-S编译成一个程序集列表。很容易看出虚拟分派(运行时)和正常函数调用之间的区别。@Mikael:“虚拟分派…正常函数调用”-如果这些是选项,从性能的角度看,这真的不重要:重要的是虚拟分派与内联。我担心您误解了虚拟分派:
Base::show()
不应该调用show()。。。当虚拟重载可用时不调用它,直接调用派生类实现<代码>派生::show()
不应调用
show()
——这将无限递归,直到堆栈空间耗尽。在这种情况下,如果您希望得到静态多态性的保证,无论编译器内部的魔法程度如何,您都可以使用CRTP(奇怪的循环模板模式)。如果您希望得到保证,当然,但在现实生活中,CRTP并不总是一个选项,而且设备化也不是你应该依赖的东西——如果编译器能为你做的话,它可能会很好