如何自定义C++异常的错误信息 重载what()方法最佳实践

c++++中,自定义异常错误信息的推荐做法是继承std::exception并重载what()方法。1. 创建一个继承自std::exception的类,并添加用于存储错误信息的std::string成员变量;2. 在构造函数中接收错误信息字符串并初始化该成员变量;3. 重写what()方法,返回该成员变量的c_str(),确保指针生命周期有效;4. 可选地,为异常类添加额外上下文信息(如文件名、行号、错误码)并在构造函数中初始化;5. 可选择在what()中整合上下文信息或提供独立getter方法以支持结构化访问。这种方式不仅保持与标准异常处理机制兼容,还提升了错误信息的可读性和程序对异常的精细控制能力。

如何自定义C++异常的错误信息 重载what()方法最佳实践

在C++中,要自定义异常的错误信息,最直接且推荐的做法是继承自

std::exception

基类,然后重载其虚函数

what()

。这个方法允许你提供一个描述性的C风格字符串,解释异常发生的原因,并且能与C++标准库的异常处理机制无缝集成。

如何自定义C++异常的错误信息 重载what()方法最佳实践

解决方案

自定义C++异常并重载

what()

方法,核心在于创建一个继承自

std::exception

的类。在这个类中,你可以定义自己的构造函数来接收一个错误信息字符串,并将其存储为类的成员。然后,重写

what()

方法,让它返回这个存储的错误信息。

以下是一个基本的实现思路:

立即学习“C++免费学习笔记(深入)”;

如何自定义C++异常的错误信息 重载what()方法最佳实践

#include #include #include #include  // For std::runtime_error example// 自定义异常类class MyCustomException : public std::exception {private:    std::string message_; // 存储具体的错误信息public:    // 构造函数,接收错误信息    explicit MyCustomException(const std::string& msg) : message_(msg) {}    // 重载what()方法,返回错误信息    // 注意:noexcept是C++11引入的,表示该函数不会抛出异常    // 返回的const char* 必须在异常对象的生命周期内有效    const char* what() const noexcept override {        return message_.c_str();    }    // 也可以添加其他方法来获取更详细的上下文信息    // 例如:int getErrorCode() const;};// 示例函数,可能抛出自定义异常void process_data(int value) {    if (value < 0) {        // 抛出带有特定错误信息的自定义异常        throw MyCustomException("输入值不能为负数: " + std::to_string(value));    }    // 模拟其他处理...    std::cout << "数据处理成功: " << value << std::endl;}int main() {    try {        process_data(10);        process_data(-5); // 这里会抛出异常    } catch (const MyCustomException& e) {        // 捕获自定义异常        std::cerr << "捕获到自定义异常: " << e.what() << std::endl;    } catch (const std::exception& e) {        // 捕获其他标准异常        std::cerr << "捕获到标准异常: " << e.what() << std::endl;    } catch (...) {        // 捕获所有其他未知异常        std::cerr << "捕获到未知异常" << std::endl;    }    std::cout << "程序继续执行..." << std::endl;    return 0;}

在这个例子中,

MyCustomException

类通过构造函数接收一个

std::string

,并将其保存。

what()

方法则返回这个字符串的C风格表示。这样,当异常被捕获时,我们就可以通过

e.what()

获取到具体的、自定义的错误描述。

为什么不直接抛出

std::string

或者C风格字符串?

在我看来,直接抛出

std::string

或C风格字符串,虽然在某些极简场景下看起来方便,但从工程实践和代码可维护性角度来看,这通常不是一个好的选择。

如何自定义C++异常的错误信息 重载what()方法最佳实践

首先,它丧失了类型信息。当你

throw std::string("Error!")

时,捕获方只能写

catch (std::string& e)

。这意味着你无法通过多态的方式捕获所有类型的异常(比如,你不能写

catch (const std::exception& e)

来统一处理),也无法区分不同类型的错误。在大型项目中,错误通常有不同的类别,比如文件操作错误、网络错误、逻辑错误等,通过自定义异常类型可以清晰地分类和处理这些问题。

其次,

std::exception

提供了一个标准的接口

what()

。如果你遵守这个约定,那么无论你的具体异常类型是什么,只要它继承自

std::exception

,任何捕获

std::exception

的地方都能通过

e.what()

获取到一致的错误描述。这极大地提高了代码的通用性和可读性。想象一下,如果每个模块都抛出不同类型(

std::string

char*

、自定义结构体)的错误,异常处理代码会变得非常混乱和难以维护。

最后,内存管理也是一个考量。抛出

std::string

通常没问题,因为

std::string

本身是RAII(资源获取即初始化)的,会妥善管理内存。但如果抛出C风格字符串(

char*

),你得非常小心它的生命周期。如果返回的是一个局部变量的地址,或者一个未被正确管理的动态分配内存的地址,那就会导致悬空指针或内存泄漏。

std::exception

及其派生类内部会负责好这些细节,你只需要关注错误信息的传递。

what()

方法返回

const char*

的注意事项与内存管理?

what()

方法签名是

const char* what() const noexcept

。这里有几个关键点需要深入理解:

*`const char

返回类型**:这意味着

what()`返回的是一个指向常量字符数组的指针。你不能通过这个指针修改错误信息。更重要的是,这个指针所指向的内存必须在异常对象本身的生命周期内保持有效。

const

成员函数:表示

what()

是一个常量成员函数,它不会修改对象的状态。这意味着你可以在常量对象(包括被

const

引用捕获的异常对象)上调用它。

noexcept

关键字:这是一个非常重要的保证。

noexcept

表示这个函数承诺不会抛出任何异常。在异常处理过程中,如果

what()

本身又抛出了异常,那将导致程序立即终止(

std::terminate

)。因此,

what()

的实现必须是“绝对安全”的,不能有任何可能失败的操作,比如内存分配、文件IO等。

基于这些约束,最佳实践通常是:将错误信息存储在自定义异常类的一个

std::string

成员变量中。然后,在

what()

方法中,直接返回这个

std::string

的C风格字符串表示,即

message_.c_str()

class MyCustomException : public std::exception {private:    std::string message_; // 存储错误信息public:    explicit MyCustomException(const std::string& msg) : message_(msg) {}    const char* what() const noexcept override {        // 关键点:返回内部std::string的c_str()        // std::string保证了其内部缓冲区的生命周期与std::string对象一致        return message_.c_str();    }};

这种方式确保了

what()

返回的

const char*

所指向的内存是有效的,因为它是由

message_

这个

std::string

成员变量管理的,而

message_

的生命周期与

MyCustomException

对象本身一致。当

MyCustomException

对象被销毁时,

message_

也会被销毁,其内部的内存自然也会被释放。

常见陷阱:

返回局部变量的地址:如果你在

what()

内部创建一个临时的

std::string

,然后返回它的

c_str()

,这是错误的。因为临时

std::string

what()

函数返回后就会被销毁,其内部缓冲区也随之无效,导致返回的指针成为悬空指针。

// 错误示例const char* what() const noexcept override {    std::string temp_msg = "Error: " + message_;    return temp_msg.c_str(); // temp_msg在函数返回后销毁,指针悬空}

返回字面量字符串(不带拷贝):虽然字面量字符串生命周期是静态的,但如果你想在其中嵌入变量信息,就需要动态构造,那又回到了第一个陷阱。

// 这种简单返回字面量是安全的,但无法自定义内容const char* what() const noexcept override {    return "Generic error.";}

如何在自定义异常中包含更多上下文信息?

仅仅一个简单的错误信息字符串,在很多复杂的场景下可能远远不够。当异常发生时,我们往往需要知道更多上下文信息来定位问题,比如:哪个文件出了问题?哪一行代码?具体的错误码是什么?操作的用户是谁?时间戳是多少?

为了在自定义异常中包含这些更丰富的上下文信息,我们可以为异常类添加额外的成员变量,并在构造函数中接收这些信息。然后,我们可以选择几种方式来暴露这些信息:

what()

方法中整合所有信息:这是最直接的方式。你可以在

what()

的实现中,将所有相关的上下文信息拼接成一个更长的、更详细的错误字符串。

#include #include #include #include  // 用于字符串拼接class FileOperationException : public std::exception {private:    std::string message_;    std::string filename_;    int line_number_;    int error_code_; // 比如系统错误码public:    FileOperationException(const std::string& msg, const std::string& filename, int line, int err_code)        : message_(msg), filename_(filename), line_number_(line), error_code_(err_code) {}    const char* what() const noexcept override {        std::ostringstream oss;        oss << "文件操作错误: " << message_            << " (文件: " << filename_            << ", 行: " << line_number_            << ", 错误码: " << error_code_ << ")";        // 注意:这里需要将拼接后的字符串存储起来,不能直接返回临时对象的c_str()        // 最佳实践是,让message_存储完整的拼接字符串        // 为了演示,这里假设message_已经包含了所有信息        return message_.c_str(); // 假设message_在构造时就已拼接好    }    // 为了避免what()内部拼接导致的问题,通常会在构造函数或一个内部辅助函数中完成拼接    // 或者,更好的方法是提供getter,让外部按需获取详细信息    // 这里只是为了演示在what()中包含更多信息的概念,实际代码中message_应该在构造函数中完成拼接};// 改进后的FileOperationException,在构造函数中拼接what()信息class ImprovedFileOperationException : public std::exception {private:    std::string full_message_; // 存储what()的完整信息    std::string filename_;    int line_number_;    int error_code_;    // 辅助函数,用于构建完整的错误信息    std::string build_full_message(const std::string& msg, const std::string& filename, int line, int err_code) {        std::ostringstream oss;        oss << "文件操作错误: " << msg            << " (文件: " << filename            << ", 行: " << line            << ", 错误码: " << err_code << ")";        return oss.str();    }public:    ImprovedFileOperationException(const std::string& msg, const std::string& filename, int line, int err_code)        : full_message_(build_full_message(msg, filename, line, err_code)),          filename_(filename), line_number_(line), error_code_(err_code) {}    const char* what() const noexcept override {        return full_message_.c_str();    }    // 提供独立的getter方法,让捕获者可以结构化地访问这些信息    const std::string& getFilename() const { return filename_; }    int getLineNumber() const { return line_number_; }    int getErrorCode() const { return error_code_; }};void read_config(const std::string& path) {    // 模拟文件读取失败    if (path == "invalid.conf") {        throw ImprovedFileOperationException("无法打开配置文件", path, __LINE__, 1001);    }    std::cout << "成功读取配置文件: " << path << std::endl;}int main_context_info() {    try {        read_config("valid.conf");        read_config("invalid.conf");    } catch (const ImprovedFileOperationException& e) {        std::cerr << "捕获到文件操作异常: " << e.what() << std::endl;        std::cerr << "详细信息 - 文件: " << e.getFilename()                  << ", 行: " << e.getLineNumber()                  << ", 错误码: " << e.getErrorCode() << std::endl;    } catch (const std::exception& e) {        std::cerr << "捕获到标准异常: " << e.what() << std::endl;    }    return 0;}

提供独立的Getter方法:这是我个人更倾向的方式。虽然

what()

提供了一个通用的字符串描述,但在程序中,你可能需要根据错误码来做分支判断,或者根据文件名来记录日志。仅仅解析

what()

返回的字符串是低效且容易出错的。因此,为每个上下文信息提供独立的getter方法,可以让捕获者以结构化的方式访问这些数据,而不是依赖字符串解析

在上面的

ImprovedFileOperationException

示例中,我就同时提供了

what()

方法返回一个详细的字符串,也提供了

getFilename()

getLineNumber()

getErrorCode()

等getter方法。这样,无论是人类阅读还是程序逻辑判断,都能获得所需的信息。

选择哪种方式取决于你的需求。如果只是为了日志记录或给用户看,

what()

中包含所有信息就足够了。但如果你的程序需要根据异常的特定属性进行更细粒度的处理,那么提供独立的getter方法会是更好的选择。

以上就是如何自定义C++异常的错误信息 重载what()方法最佳实践的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1470802.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 18:34:29
下一篇 2025年12月18日 18:34:45

相关推荐

  • CSS mask属性无法获取图片:为什么我的图片不见了?

    CSS mask属性无法获取图片 在使用CSS mask属性时,可能会遇到无法获取指定照片的情况。这个问题通常表现为: 网络面板中没有请求图片:尽管CSS代码中指定了图片地址,但网络面板中却找不到图片的请求记录。 问题原因: 此问题的可能原因是浏览器的兼容性问题。某些较旧版本的浏览器可能不支持CSS…

    2025年12月24日
    900
  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    400
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    800
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    400
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    500
  • 为什么设置 `overflow: hidden` 会导致 `inline-block` 元素错位?

    overflow 导致 inline-block 元素错位解析 当多个 inline-block 元素并列排列时,可能会出现错位显示的问题。这通常是由于其中一个元素设置了 overflow 属性引起的。 问题现象 在不设置 overflow 属性时,元素按预期显示在同一水平线上: 不设置 overf…

    2025年12月24日 好文分享
    400
  • 网页使用本地字体:为什么 CSS 代码中明明指定了“荆南麦圆体”,页面却仍然显示“微软雅黑”?

    网页中使用本地字体 本文将解答如何将本地安装字体应用到网页中,避免使用 src 属性直接引入字体文件。 问题: 想要在网页上使用已安装的“荆南麦圆体”字体,但 css 代码中将其置于第一位的“font-family”属性,页面仍显示“微软雅黑”字体。 立即学习“前端免费学习笔记(深入)”; 答案: …

    2025年12月24日
    000
  • 如何选择元素个数不固定的指定类名子元素?

    灵活选择元素个数不固定的指定类名子元素 在网页布局中,有时需要选择特定类名的子元素,但这些元素的数量并不固定。例如,下面这段 html 代码中,activebar 和 item 元素的数量均不固定: *n *n 如果需要选择第一个 item元素,可以使用 css 选择器 :nth-child()。该…

    2025年12月24日
    200
  • 使用 SVG 如何实现自定义宽度、间距和半径的虚线边框?

    使用 svg 实现自定义虚线边框 如何实现一个具有自定义宽度、间距和半径的虚线边框是一个常见的前端开发问题。传统的解决方案通常涉及使用 border-image 引入切片图片,但是这种方法存在引入外部资源、性能低下的缺点。 为了避免上述问题,可以使用 svg(可缩放矢量图形)来创建纯代码实现。一种方…

    2025年12月24日
    100
  • 如何让“元素跟随文本高度,而不是撑高父容器?

    如何让 元素跟随文本高度,而不是撑高父容器 在页面布局中,经常遇到父容器高度被子元素撑开的问题。在图例所示的案例中,父容器被较高的图片撑开,而文本的高度没有被考虑。本问答将提供纯css解决方案,让图片跟随文本高度,确保父容器的高度不会被图片影响。 解决方法 为了解决这个问题,需要将图片从文档流中脱离…

    2025年12月24日
    000
  • 为什么我的特定 DIV 在 Edge 浏览器中无法显示?

    特定 DIV 无法显示:用户代理样式表的困扰 当你在 Edge 浏览器中打开项目中的某个 div 时,却发现它无法正常显示,仔细检查样式后,发现是由用户代理样式表中的 display none 引起的。但你疑问的是,为什么会出现这样的样式表,而且只针对特定的 div? 背后的原因 用户代理样式表是由…

    2025年12月24日
    200
  • inline-block元素错位了,是为什么?

    inline-block元素错位背后的原因 inline-block元素是一种特殊类型的块级元素,它可以与其他元素行内排列。但是,在某些情况下,inline-block元素可能会出现错位显示的问题。 错位的原因 当inline-block元素设置了overflow:hidden属性时,它会影响元素的…

    2025年12月24日
    000
  • 为什么 CSS mask 属性未请求指定图片?

    解决 css mask 属性未请求图片的问题 在使用 css mask 属性时,指定了图片地址,但网络面板显示未请求获取该图片,这可能是由于浏览器兼容性问题造成的。 问题 如下代码所示: 立即学习“前端免费学习笔记(深入)”; icon [data-icon=”cloud”] { –icon-cl…

    2025年12月24日
    200
  • 为什么使用 inline-block 元素时会错位?

    inline-block 元素错位成因剖析 在使用 inline-block 元素时,可能会遇到它们错位显示的问题。如代码 demo 所示,当设置了 overflow 属性时,a 标签就会错位下沉,而未设置时却不会。 问题根源: overflow:hidden 属性影响了 inline-block …

    2025年12月24日
    000
  • 如何利用 CSS 选中激活标签并影响相邻元素的样式?

    如何利用 css 选中激活标签并影响相邻元素? 为了实现激活标签影响相邻元素的样式需求,可以通过 :has 选择器来实现。以下是如何具体操作: 对于激活标签相邻后的元素,可以在 css 中使用以下代码进行设置: li:has(+li.active) { border-radius: 0 0 10px…

    2025年12月24日
    100
  • 为什么我的 CSS 元素放大效果无法正常生效?

    css 设置元素放大效果的疑问解答 原提问者在尝试给元素添加 10em 字体大小和过渡效果后,未能在进入页面时看到放大效果。探究发现,原提问者将 CSS 代码直接写在页面中,导致放大效果无法触发。 解决办法如下: 将 CSS 样式写在一个单独的文件中,并使用 标签引入该样式文件。这个操作与原提问者观…

    2025年12月24日
    000
  • 如何模拟Windows 10 设置界面中的鼠标悬浮放大效果?

    win10设置界面的鼠标移动显示周边的样式(探照灯效果)的实现方式 在windows设置界面的鼠标悬浮效果中,光标周围会显示一个放大区域。在前端开发中,可以通过多种方式实现类似的效果。 使用css 使用css的transform和box-shadow属性。通过将transform: scale(1.…

    2025年12月24日
    200
  • 为什么我的 em 和 transition 设置后元素没有放大?

    元素设置 em 和 transition 后不放大 一个 youtube 视频中展示了设置 em 和 transition 的元素在页面加载后会放大,但同样的代码在提问者电脑上没有达到预期效果。 可能原因: 问题在于 css 代码的位置。在视频中,css 被放置在单独的文件中并通过 link 标签引…

    2025年12月24日
    100
  • 为什么我的 Safari 自定义样式表在百度页面上失效了?

    为什么在 Safari 中自定义样式表未能正常工作? 在 Safari 的偏好设置中设置自定义样式表后,您对其进行测试却发现效果不同。在您自己的网页中,样式有效,而在百度页面中却失效。 造成这种情况的原因是,第一个访问的项目使用了文件协议,可以访问本地目录中的图片文件。而第二个访问的百度使用了 ht…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信