编程笔记001 | C++中的异常机制与 dynamic_cast, const_cast, reinterpret_cast, static_cast 简要总结

一、C++ 中的异常机制 (exceptions)

  1. 头文件 cstdlib / stdlib.h 包含:
    1. std::abort() 可以向标准错误流 cerr 发送消息 abnormal program termination, 然后终止程序.
    2. std::exit(...) / exit(...) 刷新文件缓冲区而不显示消息.
  2. 常见异常机制:
    1. try 表示注意该代码块引发的异常.
    2. throw 类似跳转, 常见的是 throw "...".
    3. catch (type) 处理异常.
    4. 进阶版本:
      1. 可以 throw 一个类对象 (直接构造函数形式 throw classname(x, y, ...)), catch 中调用对象接口函数.
      2. 异常规范: 现在不常用. 剩下一个 noexcept 放在函数声明后面表示不会引发异常.
      3. 栈解退 (unwinding the stack): throw 完之后层层栈解退, 直到到达 try 所在那层, 然后 “搜寻” catch. 与此同时自动变量, 对象会相应析构.

        throw 语句将控制权向上返回给第一个满足如下条件的函数: 包含能够捕获相应异常的 try-catch 组合.

        • throw 自动生成副本, 但仍然建议在 catch 中使用引用. 主要原因是可以执行派生类对象.
        • catch 块的排列顺序应和类的派生顺序相反 (否则引用出问题).
    5. 头文件 exception 包含 exception 类, 用 class classname: public std::exception 可以使用其中的 what() 方法, 一般 return 一个字符串.
    6. 头文件 stdexcept 十分万能:
      1. logic_error 系列
        1. domain_error: 函数定义域.
        2. invalid_argument: 传递的字符串出错.
        3. length_error: 空间不够 (e.g. 数组).
        4. out_of_bounds: 索引无效.
      2. runtime_error 系列
        1. range_error: 除了下面两者的所有.
        2. overflow_error: 超过变量类型最大范围.
        3. underflow_error: 低于了浮点数最小表示值.
      3. bad_allocexception 公有派生.
      4. std::nowthrow, std::nothrow.
  3. 异常何时出现问题
    1. 未捕获异常
      1. 调用 terminate() 函数. (头文件 exception)
        1. terminate() 默认调用 abort() 函数, 但可以通过 set_terminate() 函数修改行为 (但必须包含一个需调用的函数).
             typedef void (*terminate_handler)();
             terminate_handler set_terminate(terminate_handler f) throw();
             terminate_handler set_terminate(terminate_handler f) noexcept;
             void terminate();
             void terminate() noexcept;
          
      2. 调用 unexpected() 函数. 可用 set_unexpected 修改 (默认调用 terminate).
    2. 一些不易引起注意的事项
      1. 如果用了 new 进行内存分配, 过早的终止会导致相应的 delete 语句不被执行, 导致内存泄漏.

        解决方案:

        • catch 语句中加入相应的 delete 语句.
        • (后文会提到) 智能指针模板.

二、与异常机制相结合: 更安全的四种类型转换

  1. RTTI (Runtime Type Identification 运行阶段类型识别)
    1. 用途: 虚函数能够 “自动” 地知道某指针指向的对象类型, 并自动调用其方法, 而如果我们需要把这一功能 “解封装”, 那就要用到 RTTI.
    2. 实现
      1. Class1ptr* c1 = dynamic_cast<Class2ptr>(var): 只会告诉你转换是否安全, 但不会告诉你具体是什么类型. 不安全返回空指针. 配合 if 可以包含检验环节.

        另外几种常见的强制类型转换:

        • static_cast: 几乎与 dynamic_cast 相同, 但是没有错误检查机制. 同时 dynamic_cast 能够检查形如 “A 同时派生了 B 和 C, B 与 C 之间的类型转换 (返回 nullptr)”, 但 static_cast 不行. 但 static_cast 还允许枚举类型和整型之间以及数值类型之间的转换。

          注: 这两种转换在开发人员进行安全保证的情况下还可以脱离类来转换 (int -> char, etc.).

        • reinterpret_cast: “近乎万能”, 不关心类的继承关系.

        • const_cast: 把常量指针 / 引用转换为非常量指针 / 引用 (好处是确保了大部分时候的安全性).

      2. typeid (用到头文件 typeinfo) 重载了运算符 ==!=, 常见有:
        1. typeid(var1 / typename1) == typeid(var2 / typename2)
        2. typeid(varname).name()

        重要提示: 如果发现在扩展的 if else 语句中使用了 typeid, 则应考虑是否应该使用虚函数和 dynamic_cast.

参考文献: C++ Primer Plus, 6th edition

欢迎关注我的博客!
Find me on GitHub: GitHub profile page
Gitee account (under construction): Gitee site
GitLab account (under construction): GitLab site
Also find me on Luogu:Luogu profile
欢迎大家关注我,在项目上与我协作哦!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值