慕的地6264312
不用猜测,我决定实际上是用一小段C ++代码和稍旧的Linux安装程序来查看生成的代码。class MyException{public: MyException() { } ~MyException() { }};void my_throwing_function(bool throwit){ if (throwit) throw MyException();}void another_function();void log(unsigned count);void my_catching_function(){ log(0); try { log(1); another_function(); log(2); } catch (const MyException& e) { log(3); } log(4);}我使用进行了编译g++ -m32 -W -Wall -O3 -save-temps -c,然后查看了生成的程序集文件。 .file "foo.cpp" .section .text._ZN11MyExceptionD1Ev,"axG",@progbits,_ZN11MyExceptionD1Ev,comdat .align 2 .p2align 4,,15 .weak _ZN11MyExceptionD1Ev .type _ZN11MyExceptionD1Ev, @function_ZN11MyExceptionD1Ev:.LFB7: pushl %ebp.LCFI0: movl %esp, %ebp.LCFI1: popl %ebp ret.LFE7: .size _ZN11MyExceptionD1Ev, .-_ZN11MyExceptionD1Ev_ZN11MyExceptionD1Ev是MyException::~MyException(),因此编译器决定需要析构函数的非内联副本。.globl __gxx_personality_v0.globl _Unwind_Resume .text .align 2 .p2align 4,,15.globl _Z20my_catching_functionv .type _Z20my_catching_functionv, @function_Z20my_catching_functionv:.LFB9: pushl %ebp.LCFI2: movl %esp, %ebp.LCFI3: pushl %ebx.LCFI4: subl $20, %esp.LCFI5: movl $0, (%esp).LEHB0: call _Z3logj.LEHE0: movl $1, (%esp).LEHB1: call _Z3logj call _Z16another_functionv movl $2, (%esp) call _Z3logj.LEHE1:.L5: movl $4, (%esp).LEHB2: call _Z3logj addl $20, %esp popl %ebx popl %ebp ret.L12: subl $1, %edx movl %eax, %ebx je .L16.L14: movl %ebx, (%esp) call _Unwind_Resume.LEHE2:.L16:.L6: movl %eax, (%esp) call __cxa_begin_catch movl $3, (%esp).LEHB3: call _Z3logj.LEHE3: call __cxa_end_catch .p2align 4,,3 jmp .L5.L11:.L8: movl %eax, %ebx .p2align 4,,6 call __cxa_end_catch .p2align 4,,6 jmp .L14.LFE9: .size _Z20my_catching_functionv, .-_Z20my_catching_functionv .section .gcc_except_table,"a",@progbits .align 4.LLSDA9: .byte 0xff .byte 0x0 .uleb128 .LLSDATT9-.LLSDATTD9.LLSDATTD9: .byte 0x1 .uleb128 .LLSDACSE9-.LLSDACSB9.LLSDACSB9: .uleb128 .LEHB0-.LFB9 .uleb128 .LEHE0-.LEHB0 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB1-.LFB9 .uleb128 .LEHE1-.LEHB1 .uleb128 .L12-.LFB9 .uleb128 0x1 .uleb128 .LEHB2-.LFB9 .uleb128 .LEHE2-.LEHB2 .uleb128 0x0 .uleb128 0x0 .uleb128 .LEHB3-.LFB9 .uleb128 .LEHE3-.LEHB3 .uleb128 .L11-.LFB9 .uleb128 0x0.LLSDACSE9: .byte 0x1 .byte 0x0 .align 4 .long _ZTI11MyException.LLSDATT9:惊喜!正常代码路径上根本没有多余的指令。相反,编译器生成了额外的离线修正代码块,这些代码块通过函数末尾的表引用(实际上放在可执行文件的单独部分中)。所有工作都是由标准库在后台基于这些表(_ZTI11MyExceptionis typeinfo for MyException)完成的。好吧,这实际上对我来说并不令人惊讶,我已经知道该编译器是如何做到的。继续汇编输出: .text .align 2 .p2align 4,,15.globl _Z20my_throwing_functionb .type _Z20my_throwing_functionb, @function_Z20my_throwing_functionb:.LFB8: pushl %ebp.LCFI6: movl %esp, %ebp.LCFI7: subl $24, %esp.LCFI8: cmpb $0, 8(%ebp) jne .L21 leave ret.L21: movl $1, (%esp) call __cxa_allocate_exception movl $_ZN11MyExceptionD1Ev, 8(%esp) movl $_ZTI11MyException, 4(%esp) movl %eax, (%esp) call __cxa_throw.LFE8: .size _Z20my_throwing_functionb, .-_Z20my_throwing_functionb在这里,我们看到了引发异常的代码。尽管没有仅仅因为可能引发异常而产生了额外的开销,但是在实际引发和捕获异常方面显然存在很多开销。其中大多数隐藏在中__cxa_throw,该必须:在异常表的帮助下遍历堆栈,直到找到该异常的处理程序为止。展开堆栈,直到到达该处理程序为止。实际调用处理程序。将其与仅返回值的成本进行比较,您会看到为什么仅将异常用于特殊收益的原因。最后,汇编文件的其余部分: .weak _ZTI11MyException .section .rodata._ZTI11MyException,"aG",@progbits,_ZTI11MyException,comdat .align 4 .type _ZTI11MyException, @object .size _ZTI11MyException, 8_ZTI11MyException: .long _ZTVN10__cxxabiv117__class_type_infoE+8 .long _ZTS11MyException .weak _ZTS11MyException .section .rodata._ZTS11MyException,"aG",@progbits,_ZTS11MyException,comdat .type _ZTS11MyException, @object .size _ZTS11MyException, 14_ZTS11MyException: .string "11MyException"typeinfo数据。 .section .eh_frame,"a",@progbits.Lframe1: .long .LECIE1-.LSCIE1.LSCIE1: .long 0x0 .byte 0x1 .string "zPL" .uleb128 0x1 .sleb128 -4 .byte 0x8 .uleb128 0x6 .byte 0x0 .long __gxx_personality_v0 .byte 0x0 .byte 0xc .uleb128 0x4 .uleb128 0x4 .byte 0x88 .uleb128 0x1 .align 4.LECIE1:.LSFDE3: .long .LEFDE3-.LASFDE3.LASFDE3: .long .LASFDE3-.Lframe1 .long .LFB9 .long .LFE9-.LFB9 .uleb128 0x4 .long .LLSDA9 .byte 0x4 .long .LCFI2-.LFB9 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI3-.LCFI2 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI5-.LCFI3 .byte 0x83 .uleb128 0x3 .align 4.LEFDE3:.LSFDE5: .long .LEFDE5-.LASFDE5.LASFDE5: .long .LASFDE5-.Lframe1 .long .LFB8 .long .LFE8-.LFB8 .uleb128 0x4 .long 0x0 .byte 0x4 .long .LCFI6-.LFB8 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI7-.LCFI6 .byte 0xd .uleb128 0x5 .align 4.LEFDE5: .ident "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)" .section .note.GNU-stack,"",@progbits甚至更多的异常处理表,以及各种额外的信息。因此,至少对于Linux上的GCC,得出的结论是:无论是否引发异常,开销都是额外的空间(用于处理程序和表),加上在引发异常时解析表并执行处理程序的额外开销。如果使用异常而不是错误代码,并且错误很少见,则错误速度会更快,因为您不再需要进行错误测试。