admin管理员组文章数量:1794759
C++属性——noreturn
自C++11以来,C++引入了很多属性,恰当的使用属性可以辅助编译器进行更多的优化,从而提高程序的性能,反之,可能会导致程序崩溃或产生未定义行为。本文将结合实例代码讲解如何正确使用noreturn属性,以确保程序的正确性和稳定性。
1. 背景
小王看到同事的代码中有部分函数被标记为noreturn,感觉挺高大上的,然后就自己模仿之,在自己的代码中也加入了相对应的标记,代码可简化如下:
代码语言:javascript代码运行次数:0运行复制#pragma once
#include<string>
class People
{
public:
People(std::string name, int age):m_name(name),m_age(age){
}
[[noreturn]] void setName(std::string name)
{
m_name = name;
}
[[noreturn]] void setAge(int age)
{
m_age = age;
}
private:
std::string m_name;
int m_age;
};
他想当然的认为noreturn属性用于标记没有返回值/或返回值为void的函数,并且自测环节(debug模式下)没出现任何问题,他还蛮开心的,又学到了一个新的知识点。
可是发布Release版本时,程序总是崩溃,排查很久也没有找到原因。小王将所有注意力集中于debug和release两种模式的区别,最后发现,在debug模式下打开优化,也会存在崩溃。进而进一步确认是开启优化后,编译器优化导致的程序崩溃。
秉承着编译器没错,自己代码有错的原则,进一步分析自己的代码,将noreturn属性删除后,程序不再崩溃。
综合如上分析可知,当函数被标记为noreturn时,一旦开启优化,可能会导致程序崩溃。
2. 走近noreturn
cppreference中对于noreturn的表述如下:noreturn 是C++11引入的一种属性,用于告诉编译器某个函数不会返回到调用者。此属性仅用于函数声明中所声明的函数名,若拥有此属性的函数返回,则行为未定义。
如上可知,noreturn标记的是函数不会返回给调用者,并不是函数没有返回值。如果函数返还给调用者,则属于未定义行为。怎样才是不会返回给调用者呢?存在如下几种场景:
- 程序终止:exit()、等函数
- 抛出异常:throw 语句
- 死循环:while(true) {} 等循环
当开启优化时,由于noreturn属性的存在,导致编译器认为该函数不会返还给调用者便进行了部分优化,例如移除某些不必要的清理代码或跳过函数返回后的执行路径,进而使得程序呈现在未开启优化时运行正常,而开启优化时程序崩溃。
3. 代码示例
由于noreturn属性仅使用函数不会返还给调用者的场景,所以noreturn属性的使用场景并不多。给出简单的代码示例如下:
代码语言:javascript代码运行次数:0运行复制#include <iostream>
#include <cstdlib> // 用于exit()
//终止程序
[[noreturn]] void terminateProgram() {
std::cerr << "Fatal error occurred. Exiting program." << std::endl;
std::exit(1); // 终止程序
}
//抛出异常
[[noreturn]] void throwException() {
throw std::runtime_error("An error occurred!");
}
//死循环
[[noreturn]] void infiniteLoop() {
while (true) {}
}
实际使用过程,可能并不简单的是如上的函数形式,也可以是成员函数等多种形式。
如上为正确使用noreturn的场景,反之,如下为错误示例代码
代码语言:javascript代码运行次数:0运行复制#include <iostream>
// 错误地标记了[[noreturn]]
[[noreturn]] void potentiallyReturn(bool condition) {
if (condition) {
std::cout << "Exiting program..." << std::endl;
std::exit(1); // 正确处理:程序终止
} else {
std::cout << "Returning to caller" << std::endl;
return; // 违反[[noreturn]]属性
}
}
int main() {
potentiallyReturn(false); // 这个调用会导致未定义行为
std::cout << "This line may not execute correctly in release mode." << std::endl;
}
在上述代码中,函数potentiallyReturn在某些情况下会返回给调用者,但它却被标记为noreturn。这种错误使用可能会导致编译器在优化时跳过return路径的清理工作,进而在release模式下引发崩溃或未定义行为。
4.noreturn使用原则
为了避免错误地使用noreturn属性,建议遵循以下原则:
- 确保函数无论如何都不会返回:仅在函数通过抛出异常或调用诸如exit()、abort()等永远不会返回的函数时,才使用noreturn。
- 审查函数的所有执行路径:在标记函数为noreturn之前,仔细检查函数的所有可能执行路径。如果任何路径有返回的可能性,禁止使用该属性。
- 避免滥用属性进行优化:noreturn虽然可以在某些情况下帮助编译器进行优化,但它并不是提升性能的主要手段。优化应以程序的正确性为前提,不应因为小幅度性能提升而冒险使用noreturn。
- 在release模式下仔细测试:因为release模式下开启了更多的编译器优化,所以在该模式下要进行充分的测试。即使在debug模式下一切正常,也不意味着release模式就不会暴露问题。
5. 总结
noreturn 是C++中的一个重要属性,但也容易引发误用。它并不意味着函数没有返回值,而是表示函数不会返回控制权给调用者。误用noreturn 可能导致编译器在release模式下进行错误优化,进而导致程序崩溃或产生未定义行为。
在使用noreturn时,务必确保函数在任何执行路径上都不会返回调用点。谨慎使用这一属性可以避免不必要的调试和崩溃风险。最后,编写高质量的代码和进行充分的测试是避免此类问题的关键。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2024-10-24,如有侵权请联系 cloudcommunity@tencent 删除程序函数优化c++编译器本文标签: C属性noreturn
版权声明:本文标题:C++属性——noreturn 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1754623818a1704502.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论