我是自学成才,所以我不熟悉很多术语。我似乎无法通过谷歌搜索找到答案:什么是"虚拟" vs a" direct"打电话到虚拟功能?
这与术语有关,而不是技术性。我正在询问何时定义一个电话"直接" vs"虚拟"。 它不适用于vtable,也不属于与这些概念的实现有关的任何其他内容。
答案 0 :(得分: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
请注意,您经常可以听到有人说通过直接对象进行的虚拟功能调用是“非虚拟”。但是,语言规范不支持这种观点。根据该语言,对虚函数的所有非限定调用都是相同的:它们根据对象的动态类型进行解析。在[概念]意义上,它们都是虚拟。
在实现级别,术语“虚拟调用”通常是指通过某些实现定义的机制调度的调用,该机制实现虚拟函数的标准所需功能。通常,它通过与呼叫中使用的对象相关联的虚拟方法表(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调度)编译时的对象。在上面的简单示例中,任何现代编译器都应该能够将所有调用实现为直接调用。
答案 1 :(得分:5)
假设你有这个课程:
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
这里有online simulation代码。您将在第一种情况下看到生成的程序集调用函数的地址,而在第二种情况下,编译器将某些内容加载到寄存器中并使用此寄存器进行间接调用(即调用的地址不是"硬连接"并将在运行时动态确定)。