据说虚拟功能无法内联。如果一个函数被声明为虚拟,它不能在代码中的任何地方内联,或者仅在某些情况下是否适用? (例如,从基指针调用方法与引用上的调用方法等)。
答案 0 :(得分:8)
不,虚拟功能确实可以内联。虚拟分派仅在多态调用虚拟方法时使用(即,在指针或对象的引用上)。但是,当在对象值上调用虚方法时,不使用虚拟分派,并且编译器可以根据需要自由内联。
答案 1 :(得分:6)
假设:
struct T {
virtual void foo() { /* something */ }
};
使用多态(如果您通过foo()
或pointer-to-T
致电reference-to-T
)
T* ptr = get_ptr_somehow();
ptr->foo();
如果编译器知道 T
是其继承树中唯一的节点,它可以放弃虚拟调用并可能内联函数。然而,这是一个非常不可思议的场景,我怀疑它是否可以被检测到。在所有其他情况下,由于运行时调度,内联是不实际的。
静态调度(如果您在沼泽标准对象上调用foo()
)
T obj;
obj.foo();
在这种情况下,编译器知道obj
的动态类型是T
,它不需要虚拟分派,因此如果需要,可以内联函数代码。
T* ptr = get_ptr_somehow();
ptr->T::foo();
在这种情况下,编译器不知道obj
的动态类型,但它知道它将调用哪个函数,知道它不需要虚拟调度,因此可以内联函数代码它想要。
答案 2 :(得分:3)
虚拟成员函数基本上是指向函数的指针,该函数指向要调用的正确函数,具体取决于类层次结构实现。 (请注意,它不必是一个指针,但它通常是这样实现的,因为它似乎是这种抽象的最有效的实现。)
这表明只有在运行时才能确定将要调用的函数。
那么如何内联一个只在运行时知道的函数呢?不可能的。
除非你有一个JIT编译器:) - the_drow
真。不常见,你可能会在启动时失去一些性能,但仍然是正确的。
答案 3 :(得分:2)
答案 4 :(得分:1)
当涉及优化时,很少总是如此。通常,如果编译器无法分辨对象的实际类型,则必须在运行时解析虚函数。如果你有一个实际的实例(Class vs. Class&),那么编译器可以肯定。如果你有参考,那就必须猜测;如果调用函数本身是内联的,它可能会识别那种静态类型,也可能不识别。
请注意,在这种情况下,指针和引用之间没有区别。在任何一种情况下,您都会获得运行时多态性。
答案 5 :(得分:0)
可以内联虚函数(方法)。请记住,inline
关键字是对编译器的建议,而不是要求。
内联代码非常类似于在可执行文件中需要的地方粘贴函数。未创建独立功能。
当存在指向内联函数的指针(由编译器在内部或由程序员在外部)时,可能不会使用内联。当有指向它的指针时,必须有一个内联函数的实例。编译器可以自由地内联代码,但是在静态(固定)位置必须有一个实例来满足指针。
我的建议是在不使用分析器的情况下内联简单的,少于5行代码的方法。请记住,内联通常是将代码放在头文件中。更改方法代码后,必须重建所有依赖于头文件的翻译单元。这在大型系统中可能成本很高。一般来说,大块的代码只能在之后内联进行验证并且只是为了提高分析器指示的效率。