【发布时间】:2013-05-26 03:32:52
【问题描述】:
我想确保在生成 exe/库之前,我了解我的代码实际上被编译成什么。我有以下用 C++98 编写的程序。 这源于这个网站http://www.phpcompiler.org/articles/virtualinheritance.html .
#include <stdio.h>
class top
{
public:
int t;
};
class left : virtual public top
{
public:
int l;
};
class right : virtual public top
{
public:
int r;
};
class bottom : public left, public right
{
public:
int b;
};
int main()
{
bottom *b = new bottom();
b->l = 5;
left *l = b;
printf("%d\n", l->l);
}
用g++ -S main.cpp 编译的程序集输出如下,cmets 关于我认为它应该如何分解(这是我需要接受一些教育的地方)以及一些明确标记的问题。回答下面代码中的问题就是我要找的。p>
.file "main.cpp"
.section .text._ZN3topC2Ev,"axG",@progbits,_ZN3topC5Ev,comdat
.align 2
.weak _ZN3topC2Ev
.type _ZN3topC2Ev, @function
_ZN3topC2Ev:
.LFB3:
.cfi_startproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq %rbp ; LFB3 Associated with the address for the class top constructor
.cfi_def_cfa_offset 16 ;
.cfi_offset 6, -16 ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for t).
movq %rsp, %rbp ; onto the stack
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size _ZN3topC2Ev, .-_ZN3topC2Ev
.weak _ZN3topC1Ev
.set _ZN3topC1Ev,_ZN3topC2Ev
.section .text._ZN4leftC2Ev,"axG",@progbits,_ZN4leftC2Ev,comdat
.align 2
.weak _ZN4leftC2Ev
.type _ZN4leftC2Ev, @function
_ZN4leftC2Ev:
.LFB6:
.cfi_startproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq %rbp ; LFB6 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16 ;
.cfi_offset 6, -16 ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for l).
movq %rsp, %rbp ; onto the stack
.cfi_def_cfa_register 6 ;
movq %rdi, -8(%rbp) ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t).
movq %rsi, -16(%rbp) ;
movq -16(%rbp), %rax ; What does the rest of this do?
movq (%rax), %rdx
movq -8(%rbp), %rax
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size _ZN4leftC2Ev, .-_ZN4leftC2Ev
.section .text._ZN5rightC2Ev,"axG",@progbits,_ZN5rightC2Ev,comdat
.align 2
.weak _ZN5rightC2Ev
.type _ZN5rightC2Ev, @function
_ZN5rightC2Ev:
.LFB9:
.cfi_startproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq %rbp ; LFB9 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16 ;
.cfi_offset 6, -16 ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for r).
movq %rsp, %rbp ; onto the stack
.cfi_def_cfa_register 6 ;
movq %rdi, -8(%rbp) ; %rdi, -16(%rbp) pushes 8 more bytes (64 bits for t).
movq %rsi, -16(%rbp) ;
movq -16(%rbp), %rax ; What does the rest of this do?
movq (%rax), %rdx
movq -8(%rbp), %rax
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE9:
.size _ZN5rightC2Ev, .-_ZN5rightC2Ev
.section .text._ZN6bottomC1Ev,"axG",@progbits,_ZN6bottomC1Ev,comdat
.align 2
.weak _ZN6bottomC1Ev
.type _ZN6bottomC1Ev, @function
_ZN6bottomC1Ev:
.LFB12:
.cfi_startproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq %rbp ; LFB12 Associated with the adress for the class left constructor
.cfi_def_cfa_offset 16 ;
.cfi_offset 6, -16 ; %rdi, -8(%rbp) pushes 8 bytes (64 bits for b).
movq %rsp, %rbp ; onto the stack
.cfi_def_cfa_register 6 ;
subq $16, %rsp ; Construct all the base objects placing t into b only once?
movq %rdi, -8(%rbp) ;
movq -8(%rbp), %rax
addq $32, %rax
movq %rax, %rdi
call _ZN3topC2Ev
movl $_ZTT6bottom+8, %edx
movq -8(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call _ZN4leftC2Ev
movl $_ZTT6bottom+16, %eax
movq -8(%rbp), %rdx
addq $16, %rdx
movq %rax, %rsi
movq %rdx, %rdi
call _ZN5rightC2Ev
movl $_ZTV6bottom+24, %edx
movq -8(%rbp), %rax
movq %rdx, (%rax)
movl $_ZTV6bottom+48, %edx
movq -8(%rbp), %rax
movq %rdx, 16(%rax)
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE12:
.size _ZN6bottomC1Ev, .-_ZN6bottomC1Ev
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushq %rbp ; Store off the base pointer
.cfi_def_cfa_offset 16 ; Debug code to trace where stack pointer is?
.cfi_offset 6, -16 ;
movq %rsp, %rbp ; Move where stack pointer is to base pointer
.cfi_def_cfa_register 6 ;
pushq %rbx ; Store off what might have been in rbx
subq $24, %rsp ; Push argc onto stack
.cfi_offset 3, -24 ;
movl $40, %edi ; Push argv onto stack
call _Znwm ; Call new
movq %rax, %rbx ; Create room for b %rax contains address of memory
movq $0, (%rbx) ; location where new returned?
movq $0, 8(%rbx) ;
movq $0, 16(%rbx) ;
movq $0, 24(%rbx) ;
movq $0, 32(%rbx) ;
movq %rbx, %rdi ; Move that data into dynamic memory?
call _ZN6bottomC1Ev ; Call the construtor of the object
movq %rbx, -32(%rbp) ;
movq -32(%rbp), %rax ; Can someone explain how this code relates to
movl $5, 8(%rax) ; the explaination at:
movq -32(%rbp), %rax ; http://www.phpcompiler.org/articles/virtualinheritance.html?
movq %rax, -24(%rbp)
movq -24(%rbp), %rax
movl 8(%rax), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
addq $24, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.weak _ZTV6bottom
.section .rodata._ZTV6bottom,"aG",@progbits,_ZTV6bottom,comdat
.align 32
.type _ZTV6bottom, @object
.size _ZTV6bottom, 48
_ZTV6bottom:
.quad 32
.quad 0
.quad _ZTI6bottom
.quad 16
.quad -16
.quad _ZTI6bottom
.weak _ZTT6bottom
.section .rodata._ZTT6bottom,"aG",@progbits,_ZTV6bottom,comdat
.align 32
.type _ZTT6bottom, @object
.size _ZTT6bottom, 32
_ZTT6bottom:
.quad _ZTV6bottom+24
.quad _ZTC6bottom0_4left+24
.quad _ZTC6bottom16_5right+24
.quad _ZTV6bottom+48
.weak _ZTC6bottom0_4left
.section .rodata._ZTC6bottom0_4left,"aG",@progbits,_ZTV6bottom,comdat
.align 16
.type _ZTC6bottom0_4left, @object
.size _ZTC6bottom0_4left, 24
_ZTC6bottom0_4left:
.quad 32
.quad 0
.quad _ZTI4left
.weak _ZTC6bottom16_5right
.section .rodata._ZTC6bottom16_5right,"aG",@progbits,_ZTV6bottom,comdat
.align 16
.type _ZTC6bottom16_5right, @object
.size _ZTC6bottom16_5right, 24
_ZTC6bottom16_5right:
.quad 16
.quad 0
.quad _ZTI5right
.weak _ZTS6bottom
.section .rodata._ZTS6bottom,"aG",@progbits,_ZTS6bottom,comdat
.type _ZTS6bottom, @object
.size _ZTS6bottom, 8
_ZTS6bottom:
.string "6bottom"
.weak _ZTI6bottom
.section .rodata._ZTI6bottom,"aG",@progbits,_ZTI6bottom,comdat
.align 32
.type _ZTI6bottom, @object
.size _ZTI6bottom, 56
_ZTI6bottom:
.quad _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad _ZTS6bottom
.long 2
.long 2
.quad _ZTI4left
.quad 2
.quad _ZTI5right
.quad 4098
.weak _ZTS5right
.section .rodata._ZTS5right,"aG",@progbits,_ZTS5right,comdat
.type _ZTS5right, @object
.size _ZTS5right, 7
_ZTS5right:
.string "5right"
.weak _ZTI5right
.section .rodata._ZTI5right,"aG",@progbits,_ZTI5right,comdat
.align 32
.type _ZTI5right, @object
.size _ZTI5right, 40
_ZTI5right:
.quad _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad _ZTS5right
.long 0
.long 1
.quad _ZTI3top
.quad -6141
.weak _ZTS4left
.section .rodata._ZTS4left,"aG",@progbits,_ZTS4left,comdat
.type _ZTS4left, @object
.size _ZTS4left, 6
_ZTS4left:
.string "4left"
.weak _ZTI4left
.section .rodata._ZTI4left,"aG",@progbits,_ZTI4left,comdat
.align 32
.type _ZTI4left, @object
.size _ZTI4left, 40
_ZTI4left:
.quad _ZTVN10__cxxabiv121__vmi_class_type_infoE+16
.quad _ZTS4left
.long 0
.long 1
.quad _ZTI3top
.quad -6141
.weak _ZTS3top
.section .rodata._ZTS3top,"aG",@progbits,_ZTS3top,comdat
.type _ZTS3top, @object
.size _ZTS3top, 5
_ZTS3top:
.string "3top"
.weak _ZTI3top
.section .rodata._ZTI3top,"aG",@progbits,_ZTI3top,comdat
.align 16
.type _ZTI3top, @object
.size _ZTI3top, 16
_ZTI3top:
.quad _ZTVN10__cxxabiv117__class_type_infoE+16
.quad _ZTS3top
.ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
.section .note.GNU-stack,"",@progbits
【问题讨论】:
-
你在 main() 中的代码没有引用 t 所以我不太清楚为什么虚拟继承对你很重要。 t 也没有被初始化(也没有任何其他的 int)。
-
b->l = 5;这将初始化 b 中的数据。
-
是的。好的,b中的l被初始化了。我的意思是,当你写 C++ 的时候,你一般要在构造函数中初始化变量成员:
top() : t() {} -
我知道这一点。我只是想更好地理解顶部引用的网站在说什么。我想不出比卷起袖子潜入水中更好的方法了。我深信不疑,但我想确保在深入了解之前得到更多澄清。
-
是的,如果你没有在任何地方设置 t,那么你不太可能看到副作用。发生的情况是,您得到一个 t 成员,即使 top 在底部定义了两次(一次通过左侧,一次通过右侧)。但是查看此 ASM 不会向您显示这一点,因为您没有对 t 做任何事情。
标签: c++ gcc multiple-inheritance gnu-assembler virtual-inheritance