在这样的TU中
#include "Foo.hpp"
int main() {
// stuff
Foo* foo{new Foo{}};
foo->foo();
// stuff
}
其中
Foo.hpp
包含
#pragma once
struct Foo {
virtual void foo(); // implmented somewhere
};
除了
Foo::foo
之外,不会发生任何调用,对吗? foo
是 virtual
并且它和类都不是 final
,所以是的,在 another TU 中可能存在 override
foo
的派生类的对象,依此类推,但是...就这个 TU 而言,我认为 foo->foo()
调用 Foo::foo()
是非常清楚的。我不明白怎么会是这样。
那为什么生成的程序集是这样的呢?
main: # @main
push rax
mov edi, 8
call operator new(unsigned long)@PLT
mov rcx, qword ptr [rip + vtable for Foo@GOTPCREL]
add rcx, 16
mov qword ptr [rax], rcx
mov rdi, rax
call Foo::foo()@PLT
xor eax, eax
pop rcx
ret
我不太了解详细,但我清楚地阅读了
vtable
。为什么它还在那里?
我预计上面的 TU 的程序集 与删除
virtual
关键字后得到的程序集相同:
main: # @main
push rax
mov edi, 1
call operator new(unsigned long)@PLT
mov rdi, rax
call Foo::foo()@PLT
xor eax, eax
pop rcx
ret
(这是CE上的示例。)
从这个其他答案我读到了
A* a = new B; a->func();
在这种情况下,编译器可以确定
指向a
对象,因此无需动态调度即可调用B
的正确版本。 […]当然,编译器是否做相应的分析取决于其各自的实现。func()
答案是否只是 Clang 没有进行分析来推断不需要运行时调度?
或者我错过了什么?也许我完全误解了大会?
virtual
成员函数始终使用 ODR。所以它必须始终被定义。