十三、异常、类型转换和 lambda

news2024/9/9 4:25:07

十三、异常、类型转换和 lambda

  • 异常
    • 异常处理的基本语法
    • 示例
    • 异常类
    • 异常规范
    • `noexcept`
    • 标准异常
    • 非异常
      • 返回值
      • 错误码
      • 输出参数
      • 全局状态(如errno)
      • 结论
    • `std::opti onal`
      • 基本用法
        • 包含头文件
        • 声明和初始化
        • 访问值
        • 修改值
        • 赋值和比较
      • 注意事项
    • 总结
  • 类型转换
    • 隐式类型转换(Automatic Type Conversion)
    • 静态类型转换(static_cast)
    • 动态类型转换(dynamic_cast)
    • 常量类型转换(const_cast)
    • C风格的类型转换
    • **总结**
  • `lambda`
    • 基本语法
    • 示例
      • 简单的lambda表达式
      • 捕获列表示例
      • 修改捕获的按值传递的变量

异常

C++ 中的异常处理是一种在程序执行期间处理错误或异常情况的机制。它允许程序在遇到错误时,不是立即崩溃或退出,而是以一种优雅的方式处理错误,可能是记录错误信息、释放资源或尝试恢复。

异常处理的基本语法

C++ 异常处理主要涉及三个关键字:trycatchthrow

  • try:标识一个代码块,在这个代码块中的代码将被监控,以便检测是否有异常被抛出。
  • catch:跟在 try 块后面,用来捕获并处理异常。可以有多个 catch 块来捕获不同类型的异常。
  • throw:用于抛出一个异常,可以是基础数据类型、自定义类型或派生自 std::exception 的类型。

示例

#include <iostream>
using namespace std;

int main() {
    try {
        // 抛出一个整数作为异常
        throw 20;
    } catch (int e) {
        // 捕获并处理整数类型的异常
        cout << "An exception occurred. Exception Nr. " << e << '\n';
    }
    return 0;
}

异常类

C++ 标准库提供了一系列的标准异常类,它们都派生自 std::exception 类。这些异常类包括:

  • std::logic_error:用于报告程序逻辑错误,如无效参数或无效操作。
  • std::runtime_error:用于报告运行时错误,如超出范围的数组访问。

异常规范

在 C++98 中,函数可以使用异常规范来声明它们可能抛出的异常类型。然而,从 C++11 开始,异常规范(除了 noexcept)被认为是弃用的,并在 C++17 中被彻底移除。

noexcept

noexcept 关键字用于指定一个函数不抛出异常。如果一个标记为 noexcept 的函数尝试抛出异常,程序将调用 std::terminate(),导致程序非正常退出。

标准异常

C++ 标准库提供了一套丰富的异常处理机制,允许开发者通过抛出(throw)和捕获(catch)异常来处理程序中的错误情况。这些异常可以是标准库定义的,也可以是用户自定义的。标准库定义了一系列标准异常类,这些类都继承自std::exception类,位于<stdexcept><exception>等头文件中。

  1. std::exception:这是所有标准异常的基类。它定义了一个名为what的虚成员函数,该函数返回一个表示异常的字符串描述。

  2. std::bad_alloc:当new操作符无法分配足够的内存时抛出。它继承自std::exception

  3. std::bad_cast:在执行动态类型转换(如dynamic_cast)失败时抛出。它继承自std::bad_exception(注意:在某些实现中,它直接继承自std::exception)。

  4. std::bad_exception(注意:这实际上不是一个常用的异常,因为某些实现中它不存在或未被广泛使用):这个异常类的用途并不明确,且在某些C++标准库实现中可能不存在。它可能是作为基类设计的,但实际上很少被直接使用。

  5. std::bad_typeid:当在typeid操作中使用了nullptr或指向非多态类型对象的指针时抛出。它继承自std::exception

  6. std::bad_weak_ptr:当尝试访问std::weak_ptr管理的对象,但该对象已被销毁时抛出。它继承自std::exception

  7. std::domain_error:当数学函数接收到一个无效参数时抛出(如,sqrt接收到一个负数)。它继承自std::logic_error

  8. std::invalid_argument:当函数接收到一个无效参数时抛出。它继承自std::logic_error

  9. std::length_error:当尝试创建一个超出其最大可能长度的对象时抛出(如,std::vector尝试扩展其大小超过max_size())。它继承自std::length_error

  10. std::logic_error:这是表示程序逻辑错误的异常的基类。std::domain_errorstd::invalid_argumentstd::length_errorstd::out_of_range都是它的子类。

  11. std::out_of_range:当尝试访问某个序列(如std::vectorstd::string)的超出其当前范围的元素时抛出。它继承自std::logic_error

  12. std::overflow_error:当算术运算的结果超出了可表示的范围时抛出(如,整数溢出)。它继承自std::runtime_error

  13. std::range_error:当函数接收到一个无效范围时抛出(尽管在标准库中没有直接继承自std::range_error的常用异常,但它被用作一个基类)。

  14. std::runtime_error:这是表示运行时错误的异常的基类。std::overflow_errorstd::underflow_error等都是它的子类。

  15. std::underflow_error:当算术运算的结果小于可表示的最小值时抛出(如,整数下溢)。它继承自std::runtime_error

  16. std::overflow_error:当算术运算的结果大于可表示的最大值时抛出(如,整数上溢)。它继承自std::runtime_error

使用这些标准异常可以让你的代码更加健壮和易于维护,因为它们为常见的错误情况提供了清晰的错误表示和统一的错误处理机制。

非异常

在C++中,错误处理是一个重要的方面,它涉及到如何优雅地处理程序中可能发生的错误情况。C++提供了几种机制来处理错误,其中非异常处理是其中一种方式。非异常处理通常依赖于返回值、错误码(error codes)、输出参数(out parameters)以及全局状态(如errno)等。

返回值

最常见的非异常错误处理方式是使用函数的返回值。函数可以返回一个特定的值来表示成功或不同类型的错误。例如,许多标准库函数返回整数值,其中0通常表示成功,而非0值表示不同类型的错误。

int readFile(const char* filename) {
    // 尝试打开文件
    if (/* 文件打开失败 */) {
        return -1; // 表示错误
    }
    // 处理文件...
    return 0; // 表示成功
}

错误码

错误码通常是一个枚举或整数,用于表示程序中发生的具体错误类型。函数可以通过输出参数返回错误码,以便调用者可以检查并采取相应的行动。

enum class ErrorCode {
    Success,
    FileNotFound,
    PermissionDenied,
    // 其他错误码...
};

ErrorCode readFile(const char* filename, std::string& content) {
    // 尝试打开文件
    if (/* 文件打开失败 */) {
        return ErrorCode::FileNotFound;
    }
    // 读取文件内容到content...
    return ErrorCode::Success;
}

输出参数

输出参数是函数参数的一种,用于从函数返回额外的信息。虽然它们不直接用于错误处理,但经常与错误码一起使用,以提供有关错误或操作结果的更多细节。

全局状态(如errno)

在C(和兼容C的C++代码)中,errno是一个全局变量,用于报告函数调用的错误状态。当某些库函数(如I/O函数)失败时,它们会设置errno以指示具体的错误类型。然而,由于errno是全局的,它可能在多线程程序中引起问题,并且不是类型安全的。因此,在C++中,更推荐使用上述其他机制。

结论

非异常错误处理在C++中仍然很有用,尤其是在需要兼容C代码或避免异常开销的场景中。然而,随着C++的发展,异常处理已成为一种更受推荐的方式,因为它提供了一种结构化和类型安全的错误处理机制。在设计新的C++系统时,应优先考虑使用异常处理,但在需要时也可以使用非异常错误处理机制。

std::opti onal

std::optional 是 C++17 标准库中引入的一个非常有用的特性,它提供了一种可能包含或不包含值的包装类型。std::optional 可以包含其模板参数所指定的类型的值,或者不包含任何值(表示为“无状态”或“空”状态)。这使得函数能够返回一个值或者表示没有值返回的情况,而不需要使用特殊的错误码、指针、特殊的返回值(如使用 -1 表示错误或 nullptr 表示空),或者抛出异常。

基本用法

包含头文件

要使用 std::optional,你需要包含头文件 <optional>

#include <optional>
声明和初始化

你可以声明一个 std::optional 类型的变量,并给它赋值:

std::optional<int> maybeInt; // 默认构造,初始化为不包含值的状态
std::optional<int> maybeInt2 = 42; // 直接初始化,包含值 42
std::optional<int> maybeInt3{42}; // 列表初始化,同样包含值 42
访问值

你可以使用 * 操作符来访问 std::optional 中包含的值,但首先你需要检查它是否确实包含了一个值,这可以通过 has_value() 成员函数来完成:

if (maybeInt2.has_value()) {
    int value = *maybeInt2; // 安全地访问值
}

或者使用 value() 成员函数,如果 std::optional 不包含值,则 value() 会抛出一个 std::bad_optional_access 异常:

try {
    int value = maybeInt2.value(); // 直接访问值,如果不存在则抛出异常
} catch (const std::bad_optional_access& e) {
    // 处理异常
}
修改值

如果你想要修改 std::optional 中的值(如果它存在的话),你可以使用 emplace()value()(如果你确定它包含值):

if (maybeInt2.has_value()) {
    maybeInt2.value() = 100; // 修改已存在的值
}

// 或者使用 emplace() 来重新构造值
maybeInt2.emplace(200); // 不管之前是否有值,都会用 200 重新构造
赋值和比较

std::optional 支持赋值操作,包括从另一个 std::optional 赋值,以及从内部类型的值赋值(这将导致 std::optional 变为包含该值的状态):

std::optional<int> anotherInt = maybeInt2; // 从另一个 optional 赋值
std::optional<int> yetAnotherInt = 300; // 直接从 int 赋值

if (anotherInt == yetAnotherInt) {
    // 比较两个 optional
}

注意事项

  • 使用 std::optional 时,要特别注意空状态的检查,以避免解引用空 std::optional 导致的未定义行为。
  • std::optional 的引入旨在提供一种更优雅、更类型安全的方式来处理可选值,尤其是在函数返回类型中。
  • 虽然 std::optional 在 C++17 中引入,但许多现代编译器和库都提供了对它的支持,甚至在 C++17 正式发布之前。然而,如果你使用的是较旧的编译器或库,可能需要寻找替代方案或更新你的工具链。

总结

异常处理是 C++ 中一个重要的特性,它提供了一种结构化的方法来处理错误和异常情况。通过合理使用 trycatchthrow,以及利用标准异常类,可以使代码更加健壮和易于维护。

类型转换

在C++中,类型转换是一种将变量从一种类型转换为另一种类型的过程。C++提供了几种不同的类型转换方式,包括隐式类型转换(自动类型转换)、静态类型转换(static_cast)、动态类型转换(dynamic_cast)、常量类型转换(const_cast)以及C风格的类型转换(如 (type)valuetype(value))。每种转换方式都有其特定的用途和限制。

隐式类型转换(Automatic Type Conversion)

隐式类型转换是编译器自动进行的类型转换,通常发生在赋值操作、算术运算或函数调用时。例如,将一个整数赋值给浮点数变量时,整数会被隐式转换为浮点数。

int a = 5;
double b = a; // 隐式转换,a 从 int 转换为 double

静态类型转换(static_cast)

static_cast用于基本数据类型之间的转换,以及有明确定义转换关系的类之间的转换(如派生类到基类的转换,但注意基类指针或引用不能直接转换为派生类指针或引用,除非使用了dynamic_cast)。

double d = 3.14;
int i = static_cast<int>(d); // 将 double 转换为 int

动态类型转换(dynamic_cast)

dynamic_cast主要用于安全地将基类指针或引用转换为派生类指针或引用。如果转换失败,转换结果将是一个空指针(对于指针)或抛出异常(对于引用)。它主要用于处理类的继承层次结构中的向下转换(即基类到派生类)。

class Base {};
class Derived : public Base {};

Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全地将 Base* 转换为 Derived*

常量类型转换(const_cast)

const_cast用于修改类型的const属性。它可以将常量指针(或引用)转换为非常量指针(或引用),反之亦然。

const int* cpi = &someValue;
int* pi = const_cast<int*>(cpi); // 移除 const 限定符

C风格的类型转换

C风格的类型转换(如 (type)valuetype(value))是一种较为通用的类型转换方式,但不建议在C++中使用,因为它不够明确,可能会隐藏潜在的错误。它基本上可以被视为上述类型转换的简化形式,但缺乏类型检查的安全性。

double d = 3.14;
int i = (int)d; // C风格的类型转换

总结

在C++中,了解不同类型的转换及其适用场景是非常重要的。推荐使用static_castdynamic_castconst_cast等C++风格的类型转换,因为它们提供了更好的类型检查和安全性。C风格的类型转换应该尽量避免使用。

lambda

C++中的lambda表达式是一种定义匿名函数对象的方式。它们提供了一种简洁、灵活的机制来编写可以在需要函数对象的地方使用的代码块。Lambda表达式在C++11及以后的版本中引入,极大地增强了C++的表达能力。

基本语法

Lambda表达式的基本语法如下:

[capture](parameters) mutable -> return_type {
    // 函数体
}
  • capture:捕获列表,指定lambda表达式体内可以访问的外部变量。捕获列表可以为空,也可以包含变量的列表,这些变量被按值或按引用捕获。
  • parameters:参数列表,与普通函数的参数列表类似,但也可以为空。
  • mutable:一个可选的说明符,用于指定lambda表达式体内的代码可以修改被捕获的按值传递的变量的值。
  • return_type:返回类型,如果lambda表达式体中的代码块有返回语句,则需要指定返回类型(除了lambda表达式体只包含一个返回语句且编译器可以自动推导返回类型的情况)。
  • 函数体:包含lambda表达式要执行的代码。

示例

简单的lambda表达式

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用lambda表达式打印每个元素
    std::for_each(vec.begin(), vec.end(), [](int i) {
        std::cout << i << " ";
    });

    return 0;
}

捕获列表示例

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    int x = 10;
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用lambda表达式,捕获外部变量x
    std::for_each(vec.begin(), vec.end(), [&x](int i) {
        std::cout << x + i << " "; // 注意这里修改了x的值(虽然在这个例子中并没有)
    });

    return 0;
}

注意,在上面的例子中,捕获列表使用了[&x],表示按引用捕获变量x。如果你不希望lambda表达式体内部修改x的值,可以使用[x](按值捕获)。

修改捕获的按值传递的变量

#include <iostream>

int main() {
    int x = 10;

    // 使用mutable关键字允许修改捕获的按值传递的变量
    auto f = [x](int y) mutable {
        x = y; // 如果没有mutable,这里会编译错误
        std::cout << "x = " << x << std::endl;
    };

    f(20); // 输出: x = 20

    return 0;
}

在这个例子中,mutable关键字允许我们在lambda表达式体内修改捕获的按值传递的变量x

Lambda表达式是C++中一个非常强大的特性,它们使得代码更加简洁、灵活,并且易于阅读和维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1961093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推荐5款好用的将pdf翻译成中文的工具。

像word&#xff0c;PPT,Excel等这些文档如果要翻译的话&#xff0c;即使没有合适的工具也可以复制粘贴内容。可PDF有的时候是不可以编辑的&#xff0c;很难用这种方法实现翻译。但是这5款翻译工具就可以做到直接将PDF文件进行翻译。 1、365pdf在线翻译 直达&#xff1a;https:…

力扣Hot100-543二叉树的直径

给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5] 输出&a…

零基础入门转录组数据分析——机器学习算法之lasso(筛选特征基因)

零基础入门转录组数据分析——机器学习算法之lasso&#xff08;筛选特征基因&#xff09; 目录 零基础入门转录组数据分析——机器学习算法之lasso&#xff08;筛选特征基因&#xff09;1. Lasso基础知识2. Lasso&#xff08;Rstudio&#xff09;——代码实操2. 1 数据处理2. 2…

结构体的引入

结构体也是一种数据组合&#xff0c;它和数组的区别是&#xff0c;数组的元素类型是一样的数据集合体&#xff0c;如果元素类型不一样&#xff0c;就要用到结构体了 下面定义一个学生结构体 struct Student {int num;char name[32];int age;double score;char addr[32]; }; …

谷粒商城实战笔记-88~91-商品发布保存

文章目录 一&#xff0c;基本信息二&#xff0c;规格参数三&#xff0c;销售属性四&#xff0c;SKU信息五&#xff0c;代码分析1&#xff0c;Spu信息的保存2&#xff0c;Sku信息的保存 这一篇包含三节内容&#xff1a; 88-商品服务-API-新增商品-保存SPU基本信息89-商品服务-AP…

Redis缓存穿透、击穿和雪崩的理解和解决思路

Redis的缓存穿透 缓存穿透是指那些查询请求所要获取的数据既不在缓存&#xff08;Redis&#xff09;中&#xff0c;也不在数据库&#xff08;例如&#xff1a;MySQL&#xff09;中&#xff0c;因此每次请求都会直接访问数据库。这种情况通常由以下几种情形引起&#xff1a; 恶…

C++:类进阶之继承与派生

一、基本概念&#xff1a;继承、基类、派生类 继承&#xff1a;在定义一个新的类B时&#xff0c;如果该类与某个已有的类A相似 (指的是B拥有A的全部特点)&#xff0c;那么就可以把A作为一个基类&#xff0c;而把B作为基类的一个派生类 (也称子类)。 派生类&#xff1a;通过对…

微前端技术预研 - bit初体验

1.关于什么是微前端以及微前端的发展&#xff0c; 当前主流框架以及实现技术等&#xff0c;可参考这篇总结(非常全面)&#xff0c; 微前端总结&#xff1a;目录详见下图 本文内容主要针对bit框架的实时思路以及具体使用。 1.什么是Bit? &#xfeff;Bit 是可组合软件的构建…

《C语言实现各种排序算法》

文章目录 一、排序1、排序的各种方式分类 二、插入排序1、直接插入排序2、希尔排序3、希尔排序时间复杂度分析 三、选择排序1、直接选择排序2、堆排序 四、交换排序1、冒泡排序2、快速排序3、快速排序hoare找基准值4、快排挖坑法找基准值5、前后指针法6、快速排序非递归实现 五…

甄选范文“论数据分片技术及其应用”软考高级论文,系统架构设计师论文

论文真题 数据分片就是按照一定的规则,将数据集划分成相互独立、正交的数据子集,然后将数据子集分布到不同的节点上。通过设计合理的数据分片规则,可将系统中的数据分布在不同的物理数据库中,达到提升应用系统数据处理速度的目的。 请围绕“论数据分片技术及其应用”论题…

OCC BRepOffsetAPI_ThruSections使用

目录 一、BRepOffsetAPI_ThruSections简介 二、功能与特点 三、应用场景 四、示例 一、BRepOffsetAPI_ThruSections简介 在Open CASCADE Technology (OCCT) 中,BRepOffsetAPI_ThruSections 类是用来通过放样生成一个实体或者一个面壳(Shell)。当使用这个类时,isSolid 参…

具身智能,存内计算芯片应用新赛道

引言&#xff1a; 具身智能&#xff08;Emboided Al&#xff09;是指通过身体与环境的动态互动&#xff0c;实现对世界的感知、认知和行为控制的智能系统。具身智能强调的是智能体与环境的交互/学习/改变&#xff0c;而不仅仅是身体本身。具身智能的核心要素体现在智能体通过…

MySQL --- 数据类型

一、类型分类 数值类型bit(M)位类型&#xff0c;M指定位数&#xff0c;默认值1&#xff0c;范围1 - 64bool使用0和1表示真假tinyint [unsigned]带符号范围 -128~127&#xff0c;无符号范围 0~255&#xff0c;默认有符号smallint [unsigned]带符号范围 -2^15~2^15-1&#xff0c…

【网络世界】HTTPS协议

目录 &#x1f308;前言&#x1f308; &#x1f4c1; HTTP缺陷 &#x1f4c1; HTTPS &#x1f4c2; 概念 &#x1f4c2; 加密 &#x1f4c2; 加密方式 &#x1f4c1; 中间人攻击 &#x1f4c1; CA机构和证书 &#x1f4c2; 数据摘要&#xff08;数据指纹&#xff09; &…

nginx反向代理和负载均衡+安装jdk-22.0.2

ps -aux|grep nginx //查看进程 nginx 代理 nginx代理是负载均衡的基础 主机&#xff1a;192.168.118.60 这台主机只发布了web服务&#xff0c;没有做代理的任何操作 修改一下index.html中的内容 echo "this is java web server" > /usr/local/nginx/htm…

【OpenCV-Python实战项目】26-实时手部跟踪

0 介绍 目的&#xff1a;使用mediapipe库做手部的实时跟踪 检测流程&#xff1a;&#xff08;1&#xff09;手掌检测&#xff1b;&#xff08;2&#xff09;手掌特征检测 手掌特征分布&#xff1a;mediapipe手掌特征分布如下&#xff1a; 1.环境要求 后续代码运行环境&…

力扣SQL50 换座位

Problem: 626. 换座位 &#x1f468;‍&#x1f3eb; 参考题解 Code SELECT(CASEWHEN MOD(id, 2) ! 0 AND counts ! id THEN id 1WHEN MOD(id, 2) ! 0 AND counts id THEN idELSE id - 1END) AS id,student FROMseat,(SELECTCOUNT(*) AS countsFROMseat) AS seat_counts O…

电测量数据交换DLMSCOSEM组件第53部分:DLMSCOSEM应用层(中)

2.综述 (续上篇) 上篇地址:http://t.csdnimg.cn/DBKrg 2.2DLMS/COSEM应用层主要特点 2.2.1DLMS/COSEM应用层结构 DLMS/COSEM AL的主要部件是应用服务对象(ASO)。它给其服务用户提供服务(COSEM应用进程),并使用支撑层提供的服务。客户机和服务器侧都包含三个必…

Image Caption评估指标深入理解

前言&#xff1a;刚开始做图像描述的任务就整理了这些评估指标&#xff0c;时间久远有点记不清怎么具体实现原理了&#xff0c;结果面试的时候就问了这个问题&#xff0c;没答上来&#xff0c;郁闷了很久&#xff0c;还是基础不扎实&#xff0c;浅浅记录一下 文章目录 BLEUROUG…