路由器怎么做网站,盐城网站建设首选梦搏网络,邢台123信息最新招聘信息,做网站的分辨率多少目录
1.概述
2.noexcept作为说明符
3.noexcept作为运算符
4.传统throw与noexcept比较
5.原理剖析
6.总结 1.概述 在C中#xff0c;noexcept是一个关键字#xff0c;用于指定函数不会抛出异常。如果函数保证不会抛出异常#xff0c;编译器可以进行更多优化#xff0c;…目录
1.概述
2.noexcept作为说明符
3.noexcept作为运算符
4.传统throw与noexcept比较
5.原理剖析
6.总结 1.概述 在C中noexcept是一个关键字用于指定函数不会抛出异常。如果函数保证不会抛出异常编译器可以进行更多优化比如防止异常传播时的栈展开或者生成更有效的代码。 C11之前我们可以使用throw抛出异常,但是随着C11中移动语义的产生throw不能很好的解决移动语义过程中的异常处理不同于拷贝移动的过程如果出错的话不能保证原来的数据是否受到影响为此C11引入了noexcept关键字来处理这个问题。 C11后逐渐形成“函数要么可能发射异常要么保证不会发生异常”的共识。并提出了关键字noexcept用于指明函数保证自己不会发生异常。 示例如下
constexpr int myAdd(int a, int b) noexcept {return a b;
} 把noexcept关键字放在函数声明和定义的括号后表示该函数不会抛出任何异常。若函数抛出异常则程序会终止。 如果想要允许该程序抛出异常则可在noexcept后添加false
constexpr int myAdd(int a, int b) noexcept (false) {return a b;
} 此时允许函数抛出异常。
2.noexcept作为说明符 函数使用noexcept声明时会告诉编译器这个函数不会发生异常进而让编译器使用效率更高的检测代码如果真的发生异常时程序会调用terminate函数直接结束程序。 noexcept还可以接收一个bool类型的参数该参数必须是一个常量表达式用以决定函数是否可以抛出异常默认是true。如
void myFunction() noexcept {// 这个函数不会抛出异常
}//带参数
template class T
void myFunction() noexcept(std::is_classT::value){} noexcept也可以用于重载解析
void myFunction() {// 这个函数可能会抛出异常
}void myFunction() noexcept {// 这个函数不会抛出异常
} noexcept既可以表征普通函数不发射异常也可以用于表征成员函数不发射异常。
//普通函数
int add(int a, int b)noexcept
{return ab;
}//成员函数
class People {
public:
People(std::string name, int age) :m_name{ name }, m_age{ age } {}
~People()noexcept default;inline const int GetAge()const noexcept
{return m_age;
}inline const std::string GetName()const noexcept
{return m_name;
}private:std::string m_name{ };int m_age{ 0 };
};
3.noexcept作为运算符
作为运算符接收表达式参数返回bool类型判断表达式是否可以抛出异常。
void no_exception()noexcept
{throw true;
}void exception()
{throw true;
}void myfunc()
{int a10; int b10;exception();int c ab;
}int test_noexcept_oper() {std::coutstd::boolalphanoexcept(no_exception())\nnoexcept(exception())\nnoexcept(myfunc())\n;return 0;
}
移动构造函数和移动赋值运算符通常被设计为不抛出异常因为它们只是“窃取”资源而不是复制它们。在这些函数上使用noexcept可以确保它们不会抛出异常并允许编译器进行额外的优化。
class MyClass {
public: // 移动构造函数 MyClass(MyClass other) noexcept : /* 初始化列表 */ { // 实现资源的移动 } // 移动赋值运算符 MyClass operator(MyClass other) noexcept { // 实现资源的移动 return *this; }
}; 解决移动语义的风险
1遇到风险直接编译报错
templateclass T
void swap(T a,T b)
noexcept(noexcept(T(std::move(a))))
noexcept(noexcept(T(std::move(b))))
{static_assert(noexcept(T(std::move(a)))noexcept(T(std::move(b))));T tmp(std::move(a));astd::move(b);bstd::move(tmp);
}
2遇到风险改用拷贝
templateclass T
void swap_impl(T a,T b,std::integral_constantbool,true)noexcept{T tmp(std::move(a));astd::move(b);bstd::move(tmp);
}
templateclass T
void swap_impl(T a,T b,std::integral_constantbool,false){T tmp(a);ab;btmp;
}
templateclass T
void swap(T a,T b)
noexcept(noexcept(swap_impl(a,bstd::integral_constantbool,noexcept(T(std::move(a)))noexcept(a.operator(std::move(b)))())))
{swap_impl(a,b,std::integral_constantbool,noexcept(T(std::move(a)))noexcept(a.operator(std::move(b)))());
}
4.传统throw与noexcept比较 在C中传统的throw异常机制和noexcept说明符提供了两种处理运行时错误的方法它们在用途和目的上有明显的不同。下面是关于这两者之间比较的一些关键点
传统的throw异常机制 错误处理throw语句用于在代码中抛出一个异常表示发生了一个运行时错误或异常情况。这允许程序员在代码的多个位置中定义可能的错误情况并在一个集中的错误处理位置通常是catch块中处理它们。 传播性当异常被抛出时它会沿着调用栈向上传播直到找到一个能够处理该异常的catch块。如果没有找到合适的catch块程序将调用std::terminate()并终止。 灵活性异常机制允许程序员在多个不同的函数或方法之间传播错误而不必依赖返回值或错误代码来指示错误。这使得代码更加清晰和易于维护。 性能开销异常处理机制在运行时有一定的性能开销因为它涉及到堆栈展开、异常表查找和可能的函数调用对于catch块。因此在性能敏感的代码区域中频繁使用异常可能会导致性能下降。
noexcept说明符 异常保证noexcept说明符用于指示一个函数是否可能抛出异常。当函数被标记为noexcept时它承诺不会抛出任何异常除非它是由另一个noexcept(false)的函数调用引起的。 优化机会当编译器知道一个函数是noexcept时它可以执行一些优化例如消除额外的异常处理代码从而提高程序的执行速度。 提高异常安全性如果一个noexcept函数确实抛出了异常程序将调用std::terminate()并终止。这有助于确保在资源转移或关键操作期间不会因异常而导致资源泄漏或其他未定义行为。 设计约束noexcept提供了一种方式来限制函数的异常行为这在设计高性能或低延迟的系统时尤为重要。通过使用noexcept程序员可以确保某些关键操作不会因异常而中断。
比较
用途throw用于在代码中抛出异常以指示错误情况而noexcept用于指示函数是否可能抛出异常。性能throw/catch机制在运行时有一定的性能开销而noexcept则可以提高性能因为它允许编译器进行额外的优化。错误处理throw/catch提供了一种灵活的错误处理机制允许程序员在多个不同的函数或方法之间传播错误。而noexcept则更多地关注于函数的异常行为约束和性能优化。设计考虑在设计高性能或低延迟的系统时noexcept可能是一个重要的考虑因素因为它可以确保关键操作不会因异常而中断。而在设计更一般性的库或应用程序时throw/catch可能更为适用。
5.原理剖析 noexcept保证函数不会发射异常那么noexcept是如何保证的呢为了分析这个问题不妨让noexcept函数抛出异常同时让普通函数抛出异常作为对照组对比分析两个函数的行为。验证代码及行为如下
//当noexcept函数触发异常时会直接在函数内抛出异常的位置中断异常未扩散。
//已在 xxx.exe 中执行断点指令(__debugbreak()语句或类似调用)。
void no_exception()noexcept
{throw true;
}//当常规函数触发异常时会提示异常
//0x00007FFA2D8F543C 处(位于 xxxx.exe 中)有未经处理的异常:
// Microsoft C 异常: bool位于内存位置 0x0000005B28B3F444 处。
void exception()
{throw true;
} 由如上行为可知noexcept函数在触发异常时直接中断异常自然无法向外发射传递。 正是由于其不向外发射异常特性为编译器提供了更大的舞台。 更大的优化空间因为noexcept标注的函数其异常不会向外传递自然也就不存在开解调用栈开解调用栈是指在异常处理、函数返回或程序终止过程中系统自动执行的调用栈回溯和资源清理行为也就给编译器更大的优化空间。 提升性能vector的push_back函数在扩容时如果移动构造函数是noexcept形式时is_nothrow_move_constructible_v将使用移动来转移原有数据而非之前的拷贝完成再删除的方式。使用“能移动则移动必须拷贝再拷贝”的策略来提升性能。
注意事项 只有在时间维度上恒为不发射异常的函数才可标注为noexcept否则不要做出该函数noexcept的假设。 如果函数标注为noexcept则该函数调用的所有函数应也是noexcept否则不要做出该函数noexcept的假设。尽管noexcept调用非noexcept函数会通过编译但不推荐这样做。 不要为了使函数满足noexcept而修改函数大可不必。 释放内存的函数和析构函数默认为noexcept
6.总结 综上所述C11通过引入noexcept关键字、智能指针、移动语义等特性以及增强STL和其他库显著提高了C的异常安全性。这些特性使程序员能够更好地管理资源、避免资源泄漏和未定义行为并在异常发时代码的健壮性和可靠性。
noexcept