有人可以解释下面的段落,没有给出解释:
它的工作原理是,在几乎所有的C ++实现中, 虚函数用“虚函数表”实现, 这是一个函数指针数组。 这是不兼容的 使用函数内联,因为你根本就没有指针 内联代码。因此,对于大多数情况,虚拟和内联是不兼容的;编译器 将简单地忽略内联语句。 但是,有一种情况 compatible:直接访问对象而不是通过指针。
“C ++中的高性能游戏编程”,Paul Pedriana
有人可以解释为什么粗体的两个句子是这样的吗?
答案 0 :(得分:5)
它只是意味着编译器不会内联它必须在运行时解析的调用。
假设你有
struct A
{
virtual void foo() {};
};
和
void test(A* a)
{
a->foo();
}
此调用在运行时解析。由于foo()
可能已被派生类覆盖而a
可能指向派生类型的对象,因此a->foo()
无法内联 - 它将通过查找来解析。< / p>
第二个陈述意味着在某些情况下virtual
函数可以内联:
void test(A a)
{
a.foo();
}
在这种情况下,a
的类型为A
(即使传递给函数的原始对象是派生类型,因为您通过值传递而被切片) - 保证调用调用A::foo
,以便编译器可以内联。
答案 1 :(得分:4)
首先,关键字不以任何方式不兼容。该段正在讨论基本实施。特定功能调用可以是内联的,也可以是虚拟调度,但通常不是两者。要了解原因,您只需要了解内联呼叫或虚拟呼叫是什么。
内联函数调用是将被调用函数的至少一部分直接拼接到调用函数中的函数。这是一种低级优化,编译器甚至可以对未定义为inline
的函数执行此操作。但是在早期编译器很笨,可执行代码大小更重要的时候,关键字与优化功能更直接相关。
虚拟呼叫是呼叫者不确切知道将执行什么功能的呼叫。需要在运行时根据类查找(调度)函数的实现。
因此,如果该函数仅在运行时确定,则编译器不能将两个函数拼接在一起。它不知道拼接的功能是什么。
但是您可以在不进行特殊调度的情况下调用virtual
函数。如果编译器在编译时可以确定对象的实际类型,则会发生这种情况。例如,如果dog
来自pet
virtual
方法speak
,
dog fido;
fido.speak(); // direct dispatch: we know fido is a dog.
但如果我们有参考资料,情况并非如此:
bird buddy;
pet &favorite = prefer_dogs? fido : buddy;
favorite.speak(); // favorite may be a bird or dog: need virtual dispatch
大多数情况下,当您调用virtual
函数时,您可以通过虚拟调度来执行此操作。
还有另一种情况,据我所知只是理论上的:如果编译器可以确定整个类层次结构(或给定实例中的选项),它可以添加一个布尔测试favorite
是bird
还是dog
,并将两个函数调用内联为自动生成的if … else
语句中的替代。但无论如何,这只是你不应该担心的问题。
什么是重要是根据对象被定义为的类型调用virtual
函数,并且可以在头文件中定义inline
函数。这就是最重要的。
答案 2 :(得分:0)
它不是“不兼容”,只是在实际放置虚拟呼叫时,无法内联未知代码。当对函数进行静态调用时,内联工作正常。
文字形式不是很好,但如果你已经知道它想要讲什么,你可以在那里找到它。 ; - )