所以可以让系统调用纯虚函数的自定义函数[1]。这就提出了这样一个函数能做什么的问题。为GCC
Vtable for Foo
Foo::_ZTV3Foo: 5u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI3Foo)
16 0u
24 0u
32 (int (*)(...))__cxa_pure_virtual
,直接放在纯虚函数的槽中。由于函数原型void foo()
与真实签名不匹配,堆栈是否仍然相同?特别是,可以抛出一个异常并在某处捕获它并继续执行吗?
[1]在Linux中是否有_set_purecall_handler()的等号?
阅读x86-64 ABI补充以了解实际发生的情况;特别是关于调用约定。
在您的情况下,堆栈是安全的(因为调用void foo(void)
是安全的,而不是调用任何其他签名),您可能会抛出一些异常。
细节是特定于编译器和处理器的。您的hack也许可以工作——但也可能不行——但实际上是不可移植的(因为技术上是一种未定义的行为,IIUC)。
我不确定它会起作用。也许编译器会发出一个间接跳转,你会跳转到nil地址,这就是SIGSEGV
注意,虚调用只是一个间接跳转;
class Foo {
public:
virtual void bar(void) =0;
virtual ~Foo();
};
extern "C" void doit(Foo*f) {
f->bar();
}
用g++-4.9 -Wall -O -fverbose-asm -S foo.cc
生成的汇编代码为:
.type doit, @function
doit:
.LFB0:
.file 1 "foo.cc"
.loc 1 7 0
.cfi_startproc
.LVL0:
subq $8, %rsp #,
.cfi_def_cfa_offset 16
.loc 1 8 0
movq (%rdi), %rax # f_2(D)->_vptr.Foo, f_2(D)->_vptr.Foo
call *(%rax) # *_3
.LVL1:
.loc 1 9 0
addq $8, %rsp #,
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE0:
.size doit, .-doit
和我没有看到任何针对上面未绑定的虚方法的检查
在基类中定义抛出异常的虚方法要好得多,也更可移植。
您可以使用MELT自定义您的GCC编译器来满足您奇怪的需求!