"直接"与"虚拟"调用虚拟功能

ant*_*MAN 7 c++ virtual-functions terminology

我是自学成才,所以我不熟悉很多术语.我似乎无法通过谷歌搜索找到答案:什么是虚拟功能的"虚拟"与"直接"调用?

这涉及术语,而不是技术性.我要求将呼叫定义为"直接"与"虚拟".它不适用于vtable,也不属于与这些概念的实现有关的任何其他内容.

AnT*_*AnT 11

在不同的概念层面,您的问题的答案是不同的.

  • 在概念语言层面,非正式术语"虚拟调用"通常是指根据调用中使用的对象的动态类型解析的调用.根据C++语言标准,这适用于所有对虚函数的调用,但使用该函数的限定名称的调用除外.当在呼叫中使用该方法的限定名称时,该呼叫被称为"直接呼叫"

    SomeObject obj;
    SomeObject *pobj = &obj;
    SomeObject &robj = obj;
    
    obj.some_virtual_function(); // Virtual call
    pobj->some_virtual_function(); // Virtual call
    robj.some_virtual_function(); // Virtual call
    
    obj.SomeObject::some_virtual_function(); // Direct call
    pobj->SomeObject::some_virtual_function(); // Direct call
    robj.SomeObject::some_virtual_function(); // Direct call
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,您经常可以听到人们说通过直接对象调用虚拟函数是"非虚拟".但是,语言规范不支持这种观点.根据该语言,对虚函数的所有非限定调用都是相同的:它们根据对象的动态类型进行解析.在[概念]意义上,它们都是虚拟的.

  • 在实现级别,术语"虚拟调用"通常是指通过某种实现定义的机制调度的调用,该机制实现虚拟函数的标准所需功能.通常,它通过与呼叫中使用的对象相关联的虚拟方法表(VMT)来实现.但是,智能编译器只会在必要时使用VMT执行对虚函数的调用,即在编译时不知道对象的动态类型时.在所有其他情况下,编译器将努力直接调用该方法,即使该调用在概念级别上是正式的"虚拟".

    例如,大多数情况下,使用直接对象(与指针或对象的引用相对)对虚函数的调用将实现为直接调用(不涉及VMT调度).这同样适用于对对象的构造函数和析构函数的虚函数的立即调用

    SomeObject obj;
    SomeObject *pobj = &obj;
    SomeObject &robj = obj;
    
    obj.some_virtual_function(); // Direct call
    pobj->some_virtual_function(); // Virtual call in general case
    robj.some_virtual_function(); // Virtual call in general case
    
    obj.SomeObject::some_virtual_function(); // Direct call
    pobj->SomeObject::some_virtual_function(); // Direct call
    robj.SomeObject::some_virtual_function(); // Direct call
    
    Run Code Online (Sandbox Code Playgroud)

    当然,在这后一种意义上,没有什么阻止从执行编译到虚拟功能的直接调用的调用(不涉及VMT调度),如果编译器具有足够的信息来确定在编译时的动态类型的对象.在上面的简单示例中,任何现代编译器都应该能够将所有调用实现为直接调用.


Chr*_*phe 5

假设你有这个类:

class X { 
public: 
    virtual void myfunc(); 
};  
Run Code Online (Sandbox Code Playgroud)

如果为类型为X的普通对象调用虚函数,编译器将生成直接调用,即直接引用X::myfunct():

X a;         // object of known type 
a.myfunc();  // will call X::myfunc() directly    
Run Code Online (Sandbox Code Playgroud)

如果您通过指针取消引用或引用调用虚函数,则不清楚对象指向的类型将具有哪种类型.它可以是X但它也可以是从X派生的类型.然后编译器将进行虚拟调用,即使用指向函数地址的指针表:

X *pa;        // pointer to a polymorphic object  
...           // initialise the pointer to point to an X or a derived class from X
pa->myfunc(); // will call the myfunc() that is related to the real type of object pointed to    
Run Code Online (Sandbox Code Playgroud)

在这里,您可以在线模拟代码.您将看到,在第一种情况下,生成的程序集调用函数的地址,而在第二种情况下,编译器将某些内容加载到寄存器中并使用此寄存器进行间接调用(即调用的地址不是"硬" -wired"并将在运行时动态确定.

  • @DanKorn在`X a; a.myfunc();`无论后来如何重写`myfunc`,编译器都不需要使用vtable.编译器在程序运行之前具有所需的所有信息,因此它可以安全地生成调用代码.这就是为什么有些人称之为"直接"呼叫,尽管该术语是非标准的.我不知道为什么这个答案被低估了. (2认同)