下面是对该问题的一种常见回答:
首先,内联是程序员对编译器的一种建议,因此可以在在重载虚函数时在声明处加上
inline
关键字来修饰,
但是因为虚函数在运行时通过虚函数表,而内联函数在编译时进行代码嵌入,因此在编译时编译器不接受虚函数变为内联函数的建议。
如果你只是背八股,那么该问题到此为止。
但是很明显,内联是对编译器的建议,cpp reference也没有规定是否应该内联重载了的虚函数(反正我是没找到,但如果有请告诉我出处,感谢!),因此这个问题又要根据具体编译器来分析了。
测试代码:
#include <cstdio>class Base {
public:void test() {int i = 10;printf("Base::test<%p>\n", &i);}virtual void fun() {int i = 11;printf("Base::fun<%p>\n", &i);}
};class Son: public Base{
public:inline virtual void fun() override {int i = 12;printf("Son::fun<%p>\n", &i);}
};int main()
{Son son;Base base;Base * base_son = new Son;// 最大优化下:// test()没有多态性,编译器判断可以内联son.test(); // 10base.test(); // 10base_son->test(); // 10son.fun(); // 12:没有表现出多态性,内联base.fun(); // 11:没有表现出多态性,内联base_son->fun(); // 12:表现出多态性,gcc和msvc没内联,clang内联了//! 不懂clang,似乎它在编译时能判断多态,不过想想也是,似乎是这里的多态代码过于简单了,//! Base * base_son = new Son; 这一句一看就知道base_son调用具有多态性的函数时调用的是子类的重载函数,//! 还可以使用更复杂的测试代码,我就懒得搞了,毕竟这个问题和编译器相关return 0;
}
在开启最大优化时,各编译器的表现(在Compiler Explorer网站测试):
该测试并不严谨,只是菜鸡的随手一试罢了,有任何问题可以评论或私信来探讨。
在gcc中:
可以看到在开启最大优化时,虚函数是否重载为内联是取决于该虚函数是否在使用处表现出多态性,若有表现出多态性则不内联,否则内联之,
在clang中:
clang更聪明,它直接内联了展示出多态性的代码,不过似乎是这里的测试代码过于简单,使得多态性一眼丁真,需要多态性更复杂的代码来测试,但是我懒,也不懂clang
在msvc中:
msvc在汇编代码中判断了重载的虚函数调用是否展现多态性,在图中的cmp rdx rax
处,如果虚函数表第一项不是Son::fun则进行虚函数表中的函数调用,这明显就是展现了多态性进行的调用;如果没展现多态性,则执行下面的内联了的Son::fun,因此可以看出msvc的汇编代码既给出了Son::fun未内联的函数调用,也给出了Son::fun内联了的汇编代码
因此可以得出一个不是很严谨的结论:
- 在未表现出多态性时,重载的虚函数会被编译器内联;
- 在表现出多态性时,重载的虚函数不会被编译器内联,但是如果多态代码过于简单,clang则会内联;