我刚刚在VS2010上编译了以下内容(关闭了优化)。
class Shape {
public:
int x,y;
Shape() {
x=10;
y=20;
}
virtual void function1() {
cout<<"function1";
}
virtual void function2() {
cout<<"function2";
}
};
int main() {
Shape *s = new Shape();
s->function1();
s->function2();
return 0;
}
反汇编不显示与虚函数对应的代码块或对它的任何调用,因此我假设这是因为使用vftable查找虚函数的方式。我正在使用IDA Pro,因此可能无法解决此类问题。如果我错了,请纠正我。
在这方面我也有一些疑问。
答案 0 :(得分:10)
虚拟分派仅在对象的动态类型与其静态类型(指向派生类的指针到Base)不同时才会涉及。由于您甚至没有继承,并且在调用站点上没有确切的类型,为什么要在vtable中进行查找?
答案 1 :(得分:4)
由于你已经在类定义中定义了你的虚函数,我认为你的函数可能被编译器内联,因为它知道调用站点的确切类型。尝试将函数体移出类体。他们肯定应该出现在拆卸中。我怀疑他们现在可能会被链接器剥死。
答案 2 :(得分:2)
正如其他人所说:你没有任何继承,所以编译器通过消除虚拟调度而变得聪明。
建议阅读:Stroustrup的C ++设计与演变。它不会解决您的所有问题,但会为您提供一个框架,以帮助您更有效地回答或研究他们的答案。
答案 3 :(得分:1)
我没有你的编译器,这高度依赖于编译器和选项。使用g ++ 4.5,使用默认选项(并在修复代码中的一些问题之后),我已经编译成汇编(g++ -S -o test.asm test.cpp
)代码,它确实通过虚拟调度机制显示函数和调用(在{ {1}}在调用构造函数之后,它会提取vptr,对其进行偏移并通过寄存器中的值调用。)
Shape :: function2的定义(注意.weak表示内联)
main
Shape :: function1的定义(再次,.weak表示内联)
.globl __ZN5Shape9function2Ev
____.weak_definition __ZN5Shape9function2Ev
__ZN5Shape9function2Ev:
[...]
为形状定义vtable本身:
.globl __ZN5Shape9function1Ev
____.weak_definition __ZN5Shape9function1Ev
__ZN5Shape9function1Ev:
[...]
主要定义:
.globl __ZTV5Shape
.weak_definition __ZTV5Shape
.section __DATA,__const_coal,coalesced
.align 5
__ZTV5Shape:
.quad 0
.quad __ZTI5Shape # Ptr to type_info object
.quad __ZN5Shape9function1Ev # vtable[0] is Shape::function1
.quad __ZN5Shape9function2Ev # vtable[1] is Shape::function2
至于其他人一直在说编译器可以完全忽略虚拟调度或内联函数,这是真的。这个版本的g ++并不是针对那段特定的代码,而是通过删除指针(使用具有静态存储持续时间的.globl _main
_main:
[...]
movq %rax, %rbx
movq %rbx, %rdi
call __ZN5ShapeC1Ev # Call to constructor this will setup the vptr
movq %rbx, -24(%rbp)
movq -24(%rbp), %rax # load **vptr into rax i.e. *vptr[0]: Shape::function1
movq (%rax), %rax
movq (%rax), %rax
movq -24(%rbp), %rdi
call *%rax # call it
)