A * a = new B();
a->foo();
假设B派生A而foo()是虚函数。这个例子非常频繁,问题是在某些地方,据说编译器不会尝试内联这个,在其他地方则说明完全相反。
我个人看不出这个调用无法内联的原因,因为在编译时很容易告诉哪个函数被调用。
编辑1: 我知道“一般情况”,并且我也知道编译器需要很多因素来决定是否内联。如果我问编译器可能内联这个特定的调用,问题可能会更好。
我问这个的原因是this C++ FAQ引用的特别引用:
当通过指针或引用引用对象时,无法内联对虚函数的调用,因为必须动态解析调用。
答案 0 :(得分:4)
如果编译器能够以某种方式推断出a
指向的对象是B
的实例(即如果代码很简单,就像你的例子中那样),那么它就可以虚拟化呼叫 - 尽管不需要这样做。
但重点是,一般情况下,调用在运行时解决,因为您不知道(编译器也不会!)对象的动态类型是什么由a
指出。
换句话说,如果编译器不知道*a
的动态类型是什么,因此在运行时不知道应该调用哪个foo()
函数,则不可能进行虚拟化调用并在编译时内联它。
答案 1 :(得分:2)
为了澄清一下,g ++显然至少可以正确地描述虚拟呼叫的一些微不足道的情况;这与你的情况类似,只是稍微调整以获得更可读的汇编程序:)
void test(int);
class A { public: virtual int Bop() { return 1; } };
class B : public A { virtual int Bop() { return 4711; } };
int main() {
A* a = new B();
test(a->Bop());
}
$ g++ -O99 -S -c test.cpp
$ less test.s
...
main:
.LFB2:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $8, %edi
call _Znwm
movl $4711, %edi // hard coded 4711
movq $_ZTV1B+16, (%rax)
call _Z4testi // calls test()
...
答案 2 :(得分:0)
想象一下这样的翻译单位:
<强> compute.cpp:强>
#include "A.h"
int compute(A * p)
{
return p->get_some_int() * 10;
}
您如何认为虚拟函数调用可能被虚拟化?这是一个用法示例:
<强> main.cpp中:强>
#include "B.h"
#include "C.h"
int compute(A *);
int main()
{
A * p = rand() % 2 == 0 ? new B : new C;
return compute(x);
}
答案 3 :(得分:0)
你不能期望编译器内联它。这取决于编译器和选择的优化级别。鉴于B :: foo的实现是可见的,并且你在赋值和调用之间没有做任何事情,那么,是的,视觉检查表明有足够的信息可供编译器进行优化。
但它没有。
在很多其他情况下,它没有足够的信息。