C++11 noexcept

1、noexcept修饰符

  1. C++98中使用throw()进行动态异常声明
    void except_func() throw(int ,double){ … }
    在except_func函数后定义了一个动态异常声明throw(int, double)。 该声明指出了except_func可能抛出的异常类型.但是此特性很少被使用,故被弃用

  2. C++11使用noexcept取代throw()进行动态异常声明
    语法上有两种形式
    void except_func() noexcept {…}
    void except_func() noexcept(常量表达式) {…}
    常量表达式会被转化为bool类型的值,该值为true表示不会抛出异常,即相当于不带表达式的形式。

2、noexcept作用

noexcept作用之一就是阻止异常的传播和扩散。

#include <iostream>

void thowexcept(){ throw 1;}
void NoBlockThrow(){ thowexcept();}
void BlockThrow() noexcept { thowexcept();}
int main(){
    try{
        thowexcept();
    }
    catch(...){
        std::cout << "found throw" << std::endl;
    }
    
    try{
        NoBlockThrow(); //默认noexcept(false)
    }
    catch(...){
        std::cout << " NoBlockThrow "  << std::endl;
    }

    try{
        BlockThrow(); //在被noexcept声明的函数中发生异常,程序调用terminate结束程序
    }
    catch(...){
        std::cout << " BlockThrow "<< std::endl;
    }
    return 0;
}

在这里插入图片描述
虽然terminate()调用可能带来很多问题,比如无法保证析构函数正常调用,无法保证栈的自动释放等。但是很多时候暴力终止程序是最简单有效的办法。事实上,noexcept被广泛的,系统的用于c++11的标准库中,用于提高标准库的性能,以及满足一些阻止异常扩散的需求

3、noexcept操作符

noexcept作为操作符一般用于模板:
template < class T >
void noexecpt_func() noexcept( noexcept( T() ) ) {…}
noexecpt_func()是否为一个noexcept函数,取决于T()表达式是否会抛出异常。第二个exception就是一个noexcept操作符。

4、noexcept性能

因为在调用noexcept函数时不需要记录exception handler所以编译器可以生成更efficient的code(但实际编译器是否优化noexcept不一定,但理论上noexcept给了编译器更多优化的机会)。另外编译器在编译一个非noexcept的函数时有可能会生成很多冗余的代码,这些代码虽然只在出错的时候执行,但还是会对instruction cache造成影响,进而影响程序整体的performance。
下面是一段代码的noexcept(fasle)和noexcept的汇编代码比较.noexcept(fasle)生成了大量代码处理可能的异常,导致性能下降。

void foo();
struct Obj
{
    ~Obj();
};
void goo()
{
    Obj obj;
    foo();
}

4.1 noexcept(false)汇编代码

	.file	"testnoexcept.cpp"
	.text
	.section	.text.unlikely,"ax",@progbits
.LCOLDB0:
	.text
.LHOTB0:
	.p2align 4,,15
	.globl	_Z3goov
	.type	_Z3goov, @function
_Z3goov:
.LFB0:
	.cfi_startproc
	.cfi_personality 0x3,__gxx_personality_v0
	.cfi_lsda 0x3,.LLSDA0
	pushq	%rbx
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	subq	$16, %rsp
	.cfi_def_cfa_offset 32
.LEHB0:
	call	_Z3foov
.LEHE0:
	leaq	15(%rsp), %rdi
	call	_ZN3ObjD1Ev
	addq	$16, %rsp
	.cfi_remember_state
	.cfi_def_cfa_offset 16
	popq	%rbx
	.cfi_def_cfa_offset 8
	ret
.L3:
	.cfi_restore_state
	movq	%rax, %rbx
	jmp	.L2
	.globl	__gxx_personality_v0
	.section	.gcc_except_table,"a",@progbits
.LLSDA0:
	.byte	0xff
	.byte	0xff
	.byte	0x1
	.uleb128 .LLSDACSE0-.LLSDACSB0
.LLSDACSB0:
	.uleb128 .LEHB0-.LFB0
	.uleb128 .LEHE0-.LEHB0
	.uleb128 .L3-.LFB0
	.uleb128 0
.LLSDACSE0:
	.text
	.cfi_endproc
	.section	.text.unlikely
	.cfi_startproc
	.cfi_personality 0x3,__gxx_personality_v0
	.cfi_lsda 0x3,.LLSDAC0
	.type	_Z3goov.cold.0, @function
_Z3goov.cold.0:
.LFSB0:
.L2:
	.cfi_def_cfa_offset 32
	.cfi_offset 3, -16
	leaq	15(%rsp), %rdi
	call	_ZN3ObjD1Ev
	movq	%rbx, %rdi
.LEHB1:
	call	_Unwind_Resume
.LEHE1:
	.cfi_endproc
.LFE0:
	.section	.gcc_except_table
.LLSDAC0:
	.byte	0xff
	.byte	0xff
	.byte	0x1
	.uleb128 .LLSDACSEC0-.LLSDACSBC0
.LLSDACSBC0:
	.uleb128 .LEHB1-.LCOLDB0
	.uleb128 .LEHE1-.LEHB1
	.uleb128 0
	.uleb128 0
.LLSDACSEC0:
	.section	.text.unlikely
	.text
	.size	_Z3goov, .-_Z3goov
	.section	.text.unlikely
	.size	_Z3goov.cold.0, .-_Z3goov.cold.0
.LCOLDE0:
	.text
.LHOTE0:
	.ident	"GCC: (GNU) 8.4.1 20200928 (Red Hat 8.4.1-1)"
	.section	.note.GNU-stack,"",@progbits

4.2 noexcept汇编代码

	.file	"testnoexcept.cpp"
	.text
	.p2align 4,,15
	.globl	_Z3goov
	.type	_Z3goov, @function
_Z3goov:
.LFB0:
	.cfi_startproc
	subq	$24, %rsp
	.cfi_def_cfa_offset 32
	call	_Z3foov
	leaq	15(%rsp), %rdi
	call	_ZN3ObjD1Ev
	addq	$24, %rsp
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE0:
	.size	_Z3goov, .-_Z3goov
	.ident	"GCC: (GNU) 8.4.1 20200928 (Red Hat 8.4.1-1)"
	.section	.note.GNU-stack,"",@progbits

5、noexcept安全性

《深入理解c++11》中是这么说的。我这里暂时还不知道怎么理解 请有知道的小伙伴帮忙留言
在这里插入图片描述

6、什么时候使用

  1. 现在编译器在好路径上异常没有影响,noexcept可能的作用是减小体积
  2. 推荐在构造、复制等常用操作标记noexcept,这样性能提升可能会比较大。例如vector不会使用你的类move操作,除非它被标记为noexcept(有的编译器能自动推导)
  3. destructor一定用noexcept
  4. 简单的leaf function,像是int,pointer这类的getter,setter用noexcept。因为不可能出错

7、为什么不所有代码都使用noexcept

  1. 为了兼容之前的c++异常处理代码,就像c++为了兼容c语言做了的大量妥协一样。c++的做法通常是新加特性,而不是修改以前的特性。如果都采用noexcept,很多历史程序就会直接terminate(),无法正常使用。
  2. 有一些异常,我们是可以捕获并处理的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kingforyang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值