《深度探索C++对象模型》一书第二章关于编译器构建或扩展构造函数的结尾指出,对于我们C++新手有两个常见的误解:
1,任何没有定义default constructor的class,编译器都会自动替程序员合成一个出来;
2,编译器合成出来的default constructor会默认对class的数据成员进行初始化。
然而,事实上并非如此,但是有很多地方我们确实见到过这种说法呀,到底什么时候编译器会为我们做这个事情呢?书中总结了四种情况下,编译器会为了需要合成或者扩展构造函数:
1,class中有data member是某class的object,并且此object所属的class有default constructor;
2,class继承自有default constructor的base class;
3,class中定义了virtual function或者继承有virtual function,编译器需要为此类object初始化virtual function table;
4,class以virtual方式继承base class。
第二种情况是最简单的,如果class没有定义某class类型的object data member,同时没有virtual function和virtual base class,则如果此class没有定义默认构造函数,有两种情况发生:1,如果定义有其他带参数构造函数,则创建对象时必须传入参数,而不管其继承的base class是否有默认构造函数,否则会报找不到构造函数的错误;2,如果没有定义任何构造函数,并且其继承的base class有默认构造函数,则编译器会为其合成默认构造函数,但是不会对其基本数据类型的数据成员初始化。
| |
编译结果:
################################## .file "testdef.cpp"
.text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits | .file"testinh.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB5: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC2Ev leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE5: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -1(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size main, .-main .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits |
由此对比,Child Class的默认构造函数由编译器自动构造出来了。
针对第一种情况,如果class定义了某个object成员,此object所属类有默认构造函数,则:
1,如果class中无构造函数,则编译器将自动合成默认构造函数,并初始化object成员
| |
编译结果:
.file "testdef.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB5: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC1Ev leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE5: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -1(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size main, .-main .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits | .file "testinh.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB4: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC1Ev leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE4: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -1(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size main, .-main .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits |
2,若class中提供了任意构造函数,但是未对object成员初始化,则编译器将在构造函数中插入object成员的初始化操作,即扩展已有的构造函数
| |
.file "testinh.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB4: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC1Ev leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE4: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -1(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size main, .-main .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits | .file "testdef.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB4: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC1Ev leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE4: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -1(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size main, .-main .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits |
| |
.file "testdef.cpp" .section .text._ZN6ChildB4funcEv,"axG",@progbits,_ZN6ChildB4funcEv,comdat .align 2 .weak _ZN6ChildB4funcEv .type _ZN6ChildB4funcEv, @function _ZN6ChildB4funcEv: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size _ZN6ChildB4funcEv, .-_ZN6ChildB4funcEv .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq $_ZTV6ChildB+16, (%rax) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .text .globl main .type main, @function main: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -16(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size main, .-main .weak _ZTV6ChildB .section .rodata._ZTV6ChildB,"aG",@progbits,_ZTV6ChildB,comdat .align 16 .type _ZTV6ChildB, @object .size _ZTV6ChildB, 24 _ZTV6ChildB: .quad 0 .quad _ZTI6ChildB .quad _ZN6ChildB4funcEv .weak _ZTS6ChildB .section .rodata._ZTS6ChildB,"aG",@progbits,_ZTS6ChildB,comdat .type _ZTS6ChildB, @object .size _ZTS6ChildB, 8 _ZTS6ChildB: .string "6ChildB" .weak _ZTI6ChildB .section .rodata._ZTI6ChildB,"aG",@progbits,_ZTI6ChildB,comdat .align 16 .type _ZTI6ChildB, @object .size _ZTI6ChildB, 16 _ZTI6ChildB: .quad _ZTVN10__cxxabiv117__class_type_infoE+16 .quad _ZTS6ChildB .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits | .file "testinh.cpp" .section .text._ZN6ChildBC2Ev,"axG",@progbits,_ZN6ChildBC5Ev,comdat .align 2 .weak _ZN6ChildBC2Ev .type _ZN6ChildBC2Ev, @function _ZN6ChildBC2Ev: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq $_ZTV6ChildB+16, (%rax) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size _ZN6ChildBC2Ev, .-_ZN6ChildBC2Ev .section .text._ZN6ChildB4funcEv,"axG",@progbits,_ZN6ChildB4funcEv,comdat .align 2 .weak _ZN6ChildB4funcEv .type _ZN6ChildB4funcEv, @function _ZN6ChildB4funcEv: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size _ZN6ChildB4funcEv, .-_ZN6ChildB4funcEv .text .globl main .type main, @function main: .LFB4: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -16(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE4: .size main, .-main .weak _ZTV6ChildB .section .rodata._ZTV6ChildB,"aG",@progbits,_ZTV6ChildB,comdat .align 16 .type _ZTV6ChildB, @object .size _ZTV6ChildB, 24 _ZTV6ChildB: .quad 0 .quad _ZTI6ChildB .quad _ZN6ChildB4funcEv .weak _ZTS6ChildB .section .rodata._ZTS6ChildB,"aG",@progbits,_ZTS6ChildB,comdat .type _ZTS6ChildB, @object .size _ZTS6ChildB, 8 _ZTS6ChildB: .string "6ChildB" .weak _ZTI6ChildB .section .rodata._ZTI6ChildB,"aG",@progbits,_ZTI6ChildB,comdat .align 16 .type _ZTI6ChildB, @object .size _ZTI6ChildB, 16 _ZTI6ChildB: .quad _ZTVN10__cxxabiv117__class_type_infoE+16 .quad _ZTS6ChildB .weak _ZN6ChildBC1Ev .set _ZN6ChildBC1Ev,_ZN6ChildBC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits |
| |
############################################# .file "testinh.cpp" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits | .file "testdef.cpp" .section .text._ZN5BaseAC2Ev,"axG",@progbits,_ZN5BaseAC5Ev,comdat .align 2 .weak _ZN5BaseAC2Ev .type _ZN5BaseAC2Ev, @function _ZN5BaseAC2Ev: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size _ZN5BaseAC2Ev, .-_ZN5BaseAC2Ev .section .text._ZN6ChildBC1Ev,"axG",@progbits,_ZN6ChildBC1Ev,comdat .align 2 .weak _ZN6ChildBC1Ev .type _ZN6ChildBC1Ev, @function _ZN6ChildBC1Ev: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq -8(%rbp), %rax movq %rax, %rdi call _ZN5BaseAC2Ev movl $_ZTV6ChildB+24, %edx movq -8(%rbp), %rax movq %rdx, (%rax) leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size _ZN6ChildBC1Ev, .-_ZN6ChildBC1Ev .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp leaq -16(%rbp), %rax movq %rax, %rdi call _ZN6ChildBC1Ev movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .weak _ZTV6ChildB .section .rodata._ZTV6ChildB,"aG",@progbits,_ZTV6ChildB,comdat .align 16 .type _ZTV6ChildB, @object .size _ZTV6ChildB, 24 _ZTV6ChildB: .quad 0 .quad 0 .quad _ZTI6ChildB .weak _ZTT6ChildB .section .rodata._ZTT6ChildB,"aG",@progbits,_ZTV6ChildB,comdat .align 8 .type _ZTT6ChildB, @object .size _ZTT6ChildB, 8 _ZTT6ChildB: .quad _ZTV6ChildB+24 .weak _ZTS6ChildB .section .rodata._ZTS6ChildB,"aG",@progbits,_ZTS6ChildB,comdat .type _ZTS6ChildB, @object .size _ZTS6ChildB, 8 _ZTS6ChildB: .string "6ChildB" .weak _ZTI6ChildB .section .rodata._ZTI6ChildB,"aG",@progbits,_ZTI6ChildB,comdat .align 32 .type _ZTI6ChildB, @object .size _ZTI6ChildB, 40 _ZTI6ChildB: .quad _ZTVN10__cxxabiv121__vmi_class_type_infoE+16 .quad _ZTS6ChildB .long 0 .long 1 .quad _ZTI5BaseA .quad -6141 .weak _ZTS5BaseA .section .rodata._ZTS5BaseA,"aG",@progbits,_ZTS5BaseA,comdat .type _ZTS5BaseA, @object .size _ZTS5BaseA, 7 _ZTS5BaseA: .string "5BaseA" .weak _ZTI5BaseA .section .rodata._ZTI5BaseA,"aG",@progbits,_ZTI5BaseA,comdat .align 16 .type _ZTI5BaseA, @object .size _ZTI5BaseA, 16 _ZTI5BaseA: .quad _ZTVN10__cxxabiv117__class_type_infoE+16 .quad _ZTS5BaseA .weak _ZN5BaseAC1Ev .set _ZN5BaseAC1Ev,_ZN5BaseAC2Ev .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" .section .note.GNU-stack,"",@progbits |
但是对于上述说的第一种误解,我是没有真正理解的,如果没有构造函数,编译器也不自动合成,对象是如何建立的?
#include <iostream>
using namespace std;
class NoDef{
public:
int k;
void print() { cout << "k is " << k << endl;}
};
int main()
{
NoDef def;
def.print();
def.k = 2;
def.print();
def.k = 199566;
def.print();
return 0;
}
由于上方的代码可以执行,那么是否说明def对象创建了?如果为NoDef添加了默认构造函数,从编译结果看,main函数中明显调用了构造函数。理解还是不到位,继续找资料学习。