"Direct" vs "virtual" 对虚函数的调用
"Direct" vs "virtual" call to a virtual function
我自学成才,因此不熟悉很多术语。我似乎无法通过谷歌搜索找到答案:什么是对虚拟函数的"虚拟"调用和"直接"调用?
这与术语有关,而非技术性问题。我想知道什么时候打电话被定义为"直接"还是"虚拟"。它与vtables无关,也与这些概念的实现无关。
在不同的概念层面上,问题的答案是不同的。
-
在概念语言级别,非正式术语"虚拟调用"通常指根据调用中使用的对象的动态类型解析的调用。根据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调度)。在上面的简单示例中,任何现代编译器都应该能够将所有调用实现为直接调用。
假设您有这样的类:
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++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 模板函数调用
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 析构函数调用
- 成员函数调用和C++对象模型
- 使用共享指针的函数调用,其对象应为 const
- C++:编译时检查匹配的函数调用对?
- 函数调用C++中的参数太少
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 返回指向对象的指针的函数调用是否为 prvalue?
- C++ 如何重载 [] 运算符并进行函数调用
- 代码的效率. 转到和函数调用
- 是同一作用域的函数部分中的函数调用
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- 类型擦除的std::function与虚拟函数调用的开销