C++ &引用;直接;vs";虚拟的;对虚拟函数的调用

C++ &引用;直接;vs";虚拟的;对虚拟函数的调用,c++,terminology,virtual-functions,C++,Terminology,Virtual Functions,我自学成才,因此不熟悉很多术语。我似乎无法通过谷歌搜索找到答案:“虚拟”和“直接”调用虚拟函数是什么 这与术语有关,而不是技术性的。我问的是什么时候一个电话被定义为“直接”与“虚拟”。 它不涉及vtables,也不涉及与这些概念的实现有关的任何其他内容。假设您有这样一个类: class X { public: virtual void myfunc(); }; 如果为类型为X的普通对象调用虚函数,编译器将生成直接调用,即直接引用X::myfunct(): X a;

我自学成才,因此不熟悉很多术语。我似乎无法通过谷歌搜索找到答案:“虚拟”和“直接”调用虚拟函数是什么

这与术语有关,而不是技术性的。我问的是什么时候一个电话被定义为“直接”与“虚拟”。
它不涉及vtables,也不涉及与这些概念的实现有关的任何其他内容。

假设您有这样一个类:

class X { 
public: 
    virtual void myfunc(); 
};  
如果为类型为X的普通对象调用虚函数,编译器将生成直接调用,即直接引用
X::myfunct()

X a;         // object of known type 
a.myfunc();  // will call X::myfunc() directly    
如果通过指针取消引用或引用调用虚函数,则不清楚指向的对象将真正具有哪种类型。它可以是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    

在这里你有一个完整的代码。您将看到,在第一种情况下,生成的程序集调用函数的地址,而在第二种情况下,编译器加载寄存器中的某些内容,并使用该寄存器进行间接调用(即,被调用的地址不是“硬连线”的,将在运行时动态确定)

在不同的概念层次上,你的问题的答案是不同的

  • 在概念语言层面,非正式术语“虚拟调用”通常指根据调用中使用的对象的动态类型解析的调用。根据C++语言标准,除了使用函数的限定名之外,所有调用都适用于虚函数。在调用中使用方法的限定名时,该调用称为“直接调用”

    请注意,您经常会听到人们说,通过直接对象对虚拟函数的调用是“非虚拟的”。但是,语言规范不支持这种观点。根据该语言,对虚拟函数的所有非限定调用都是相同的:它们根据对象的动态类型进行解析。从这个(概念)意义上讲,它们都是虚拟的

  • 在实现级别,术语“虚拟调用”通常指通过某种实现定义的机制调度的调用,该机制实现虚拟函数的标准必需功能。通常,它是通过与调用中使用的对象关联的虚拟方法表(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
    
    当然,在后一种意义上,如果编译器有足够的信息在编译时确定对象的动态类型,那么没有什么可以阻止编译器将对虚拟函数的任何调用实现为直接调用(不涉及VMT分派)。在上面的简单示例中,任何现代编译器都应该能够实现所有直接调用


除了详细说明技术语言差异的代码之外,请提供解释。我不确定“直接”函数调用通常指的是什么。对
a.myfunc()
的调用仍然可以是对虚拟函数的调用,事实上,如果类X被更改,从而从另一个声明myfunc为虚拟的类继承,那么确切调用哪个myfunc,比如它是X的成员还是基类,肯定取决于它是否在类X中被重写“X”没有派生类,为什么它被认为是一个多态对象?同样,
final
也会直接调用它吗?@DanKorn In
xa;a.myfunc()无论以后是否重写
myfunc
,编译器都不需要使用vtable。编译器在程序运行之前拥有所需的所有信息,因此它可以安全地生成调用代码。这就是为什么可能有人将其称为“直接”“呼叫,尽管该术语不标准。我不知道为什么这个答案会被否决。好吧,如果我创建一个派生类的普通对象,并调用基类或派生类的虚拟成员函数,这是否会影响对这些虚拟函数的“直接”调用?一个普通的对象能发出一个虚拟的调用吗?你能指出你读到的关于direct的地方吗。这是我第一次听说这个词。关于编译器内联的规则。@xvan的可能重复我不同意这是一个重复,但我承认我只是不理解它是一个重复。但在这种情况下,我仍然觉得我的问题是正确的。它们在很大程度上是相互传递的,但答案并没有涉及对虚拟函数的调用何时是直接调用以及何时是虚拟调用,因为这在问题中并没有被隐式地问到。所以你的意思是,通过指向对象的指针,我可以指定哪个派生函数(或基函数)类保存我要调用的vitrual函数的版本?例如,如果我有一个指向基类的指针,并且我指定要调用基类中的[virtual]函数,而不是某个派生类的重写,那么它将成为直接调用,因为它可以在编译时解决?@antiHUMAN:是的,如果要抑制虚拟分派并调用基类版本,您可以使用限定名称来完成此操作。在fa中
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