file.cpp
#include <iostream>
class Base {
public:
virtual void f(int a ){}
};
class Derived : public Base {
public:
void f( int a ){ }
};
int main(int argc, char** argv) {
Derived obj;
Base& ref = obj;
ref.f(777);
return 0;
}
使用以下代码生成代码:g ++ -S file.cpp is:
使用以下代码生成代码:g ++ -S file.cpp is:
.file "cp.cpp"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Base::f()\n"
.section .text._ZN4Base1fEi,"axG",@progbits,_ZN4Base1fEi,comdat
.align 2
.p2align 4,,15
.weak _ZN4Base1fEi
.type _ZN4Base1fEi, @function
_ZN4Base1fEi:
.LFB1006:
.cfi_startproc
movl $10, %edx
movl $.LC0, %esi
movl $_ZSt4cout, %edi
jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
.cfi_endproc
.LFE1006:
.size _ZN4Base1fEi, .-_ZN4Base1fEi
.section .rodata.str1.1
.LC1:
.string "Derived::f()\n"
.section .text._ZN7Derived1fEi,"axG",@progbits,_ZN7Derived1fEi,comdat
.align 2
.p2align 4,,15
.weak _ZN7Derived1fEi
.type _ZN7Derived1fEi, @function
_ZN7Derived1fEi:
.LFB1007:
.cfi_startproc
movl $13, %edx
movl $.LC1, %esi
movl $_ZSt4cout, %edi
jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
.cfi_endproc
.LFE1007:
.size _ZN7Derived1fEi, .-_ZN7Derived1fEi
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB1008:
.cfi_startproc
subq $24, %rsp
.cfi_def_cfa_offset 32
movl $777, %esi
movq %rsp, %rdi
movq $_ZTV7Derived+16, (%rsp)
call _ZN7Derived1fEi
xorl %eax, %eax
addq $24, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE1008:
.size main, .-main
.p2align 4,,15
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1019:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $_ZStL8__ioinit, %edi
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, %edx
movl $_ZStL8__ioinit, %esi
movl $_ZNSt8ios_base4InitD1Ev, %edi
addq $8, %rsp
.cfi_def_cfa_offset 8
jmp __cxa_atexit
.cfi_endproc
.LFE1019:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .ctors,"aw",@progbits
.align 8
.quad _GLOBAL__sub_I_main
.weak _ZTV7Derived
.section .rodata._ZTV7Derived,"aG",@progbits,_ZTV7Derived,comdat
.align 16
.type _ZTV7Derived, @object
.size _ZTV7Derived, 24
_ZTV7Derived:
.quad 0
.quad _ZTI7Derived
.quad _ZN7Derived1fEi
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.weak _ZTI7Derived
.section .rodata._ZTI7Derived,"aG",@progbits,_ZTI7Derived,comdat
.align 16
.type _ZTI7Derived, @object
.size _ZTI7Derived, 24
_ZTI7Derived:
.quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
.quad _ZTS7Derived
.quad _ZTI4Base
.weak _ZTS7Derived
.section .rodata._ZTS7Derived,"aG",@progbits,_ZTS7Derived,comdat
.type _ZTS7Derived, @object
.size _ZTS7Derived, 9
_ZTS7Derived:
.string "7Derived"
.weak _ZTI4Base
.section .rodata._ZTI4Base,"aG",@progbits,_ZTI4Base,comdat
.align 16
.type _ZTI4Base, @object
.size _ZTI4Base, 16
_ZTI4Base:
.quad _ZTVN10__cxxabiv117__class_type_infoE+16
.quad _ZTS4Base
.weak _ZTS4Base
.section .rodata._ZTS4Base,"aG",@progbits,_ZTS4Base,comdat
.type _ZTS4Base, @object
.size _ZTS4Base, 6
_ZTS4Base:
.string "4Base"
.weakref _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
.weakref _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
.weakref _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
.weakref _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
.weakref _ZL20__gthrw_pthread_joinmPPv,pthread_join
.weakref _ZL21__gthrw_pthread_equalmm,pthread_equal
.weakref _ZL20__gthrw_pthread_selfv,pthread_self
.weakref _ZL22__gthrw_pthread_detachm,pthread_detach
.weakref _ZL22__gthrw_pthread_cancelm,pthread_cancel
.weakref _ZL19__gthrw_sched_yieldv,sched_yield
.weakref _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
.weakref _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
.weakref _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
.weakref _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
.weakref _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
.weakref _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
.weakref _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
.weakref _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
.weakref _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
.weakref _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
.weakref _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
.weakref _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
.weakref _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
.weakref _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
.weakref _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
.weakref _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
我有问题。 请告诉我在这里举行的asssembler代码中的晚期绑定。
提前感谢您的帮助。
答案 0 :(得分:3)
没有实际的&#34;后期绑定&#34;在您编译的代码中。它最终纯粹是概念性的。编译器很聪明,可以在编译时弄清楚应该将此调用分派给Derived::f
。它生成的代码直接调用Derived::f
(内部名称_ZN7Derived1fEi
)
如果你想看到实际的后期绑定,你需要提出一个不太明显的代码。一个好主意是使对象的动态类型依赖于运行时值(如rand()
调用)。
Base objb;
Derived objd;
Base& ref = rand() % 2 == 0 ? objb : objd;
ref.f(777);
答案 1 :(得分:2)
因为编译器可以弄清楚发生了什么,所以这里没有后期绑定,而是直接在这里进行调用:
movq $_ZTV7Derived+16, (%rsp)
call _ZN7Derived1fEi
这是编译器已经能够做了一段时间的事情,并且最新版本甚至进行了大量的分析,以便在不太明显的情况下尝试确定绑定。
编辑(从工作回家)。如果我们稍微编辑代码,就像这样:
class Base {
public:
virtual void f(int a ){}
};
class Derived : public Base {
public:
void f( int a ){ }
};
extern Derived& MakeObject();
int main(int argc, char** argv) {
Base& ref = MakeObject();
ref.f(777);
return 0;
}
然后编译代码,我们得到这个(来自clang++ -O2 -S virt.cpp
):
main:
pushq %rax
callq _Z10MakeObjectv
movq (%rax), %rcx
movl $777, %esi
movq %rax, %rdi
callq *(%rcx)
xorl %eax, %eax
popq %rdx
retq
此处%rcx
是虚函数的地址,%rax
包含this
值 - 然后将其放入%rdi
这是第一个参数。 %esi
是第二个参数。
或者,要了解更多内容,我们可以查看LLVM IR(中间表示):
define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 {
entry:
%call = tail call %class.Derived* @_Z10MakeObjectv()
%0 = getelementptr inbounds %class.Derived* %call, i64 0, i32 0
%1 = bitcast %class.Derived* %call to void (%class.Base*, i32)***
%vtable = load void (%class.Base*, i32)*** %1, align 8, !tbaa !1
%2 = load void (%class.Base*, i32)** %vtable, align 8
tail call void %2(%class.Base* %0, i32 777)
ret i32 0
}
在这里,您可以看到%2
在从%vtable
加载后被调用,而getelementptr
又是%1
的结果(这是LLVM描述如何在结构中查找内容的方式或数组,在vtable内部)。它从%call
和bitcast
(实际上是reinterpret_cast
变成的那个)派生Derived&
- 因为我们返回一个base&
对象,我们需要更改它在编译器内部Base*
(与getelementptr
相同)。
我喜欢LLVM / Clang这样的事情,因为LLVM IR [一旦你理解它]比最终的汇编程序代码更加神秘,更“解释” - 例如main:
subq $8, %rsp
call _Z10MakeObjectv
movq (%rax), %rdx
movq %rax, %rdi
movl $777, %esi
call *(%rdx)
xorl %eax, %eax
addq $8, %rsp
ret
通常只是变成了添加和/或乘法运算的某种组合[或者,在这种情况下,完全消失,因为我们在零偏移处获取],这就不那么明显了。
但是,g ++也提供相同的输出:
movq $_ZTV7Derived+16, anobject(%rip)
ret
作为参考,这里是派生的vtable:
_ZTV7Derived: .quad 0 .quad _ZTI7Derived .quad _ZN7Derived1fEi
请注意,vtable的加载位置为:
ZN7Derived1fEi
所以*(%rcx)确实指向函数{{1}}。