突破编程_C++_面试(异常处理)

面试题 1 :什么是异常处理?为什么需要它?

在C++中,异常处理是一种处理程序运行时错误的机制。它允许程序员在程序的某个部分中定义和处理可能会出现的异常情况,即“异常”。这些异常情况通常是由错误条件、非法操作或其他意外情况引起的。

异常处理主要包括以下三个部分:

(1)抛出异常(Throw): 当程序遇到错误或异常情况时,它会使用throw关键字抛出一个异常。throw关键字后面通常跟着一个值,这个值可以是任何数据类型,它包含了关于异常的信息。

(2)捕获异常(Catch): catch 关键字用于捕获异常。它定义了一个或多个代码块,每个代码块处理特定类型的异常。当异常被抛出时,程序会查找匹配的 catch 块来处理该异常。

(3)异常规格(Exception Specification): 这是 C++98 中引入的一个特性,用于声明函数可能抛出的异常类型。然而,在 C++11 及以后的版本中,异常规格已被弃用,推荐使用 noexcept 关键字来声明函数是否抛出异常。

为什么需要异常处理:

(1)错误处理: 异常处理提供了一种统一的机制来处理错误。相比于传统的错误代码返回值,异常处理更加直观和易于维护。

(2)资源管理: 当对象在创建或使用过程中分配了资源(如内存、文件句柄等),并且这些资源需要在对象生命周期结束时释放时,异常处理可以确保在发生异常时这些资源得到正确释放,防止资源泄漏。

(3)代码清晰: 通过将错误处理代码从正常业务逻辑代码中分离出来,异常处理可以保持代码更加清晰和易读。

(4)错误传播: 异常可以被抛出并向上传播到调用栈的更高级别,这样可以在更高层次上处理错误,而不是在每个可能出错的地方都进行错误检查。

(5)程序健壮性: 通过妥善处理异常,可以提高程序的健壮性和稳定性,减少程序崩溃或未定义行为的可能性。

总体而言,异常处理是 C++ 中一个强大的错误处理机制,它有助于编写更加健壮、清晰和可维护的代码。

面试题 2 :可以抛出什么类型的异常?

在 C++ 中,可以抛出任何类型的异常。这包括基本数据类型(如int、char、bool等)、复合数据类型(如结构体、类等)、指针类型,甚至是异常类型本身(即派生自 std::exception 的异常类)。

通常会选择抛出派生自 std::exception 的异常类型,因为这样做可以提供更多的上下文信息,并允许使用 catch 块捕获所有类型的异常(通过捕获 std::exception 类型的引用或指针)。

下面是一些示例:

(1)抛出基本数据类型:

throw 1; // 抛出一个整数

(2)抛出复合数据类型:

struct MyError 
{  int errorCode;  const char* errorMessage;  
};  void funcThrowError() 
{  MyError error = {100, "Internal Server Error"};  throw error; // 抛出一个自定义的结构体  
}

(3)抛出指针类型:

void* ptr = new int(2);  
throw ptr; // 抛出一个指针

(4)抛出派生自std::exception的异常类型:

#include <exception>  
#include <string>  class MyException : public std::exception 
{  
public:  const char* what() const throw() {  return "MyException occurred";  }  
};  void funcThrowError() 
{  throw MyException(); // 抛出一个自定义异常类  
}

在实践中,建议使用派生自 std::exception 的异常类型,因为它们提供了 what() 成员函数,该函数返回一个描述异常的字符串,这对于调试和错误报告非常有用。

面试题 3 :什么是异常规格(Exception Specification)?

异常规格(Exception Specification)是 C++98 中引入的一种特性,用于声明函数可能抛出的异常类型。它允许程序员指定函数可能抛出的异常类型,并在函数实现中确保这些约束被遵守。然而,需要注意的是,在 C++11 及以后的版本中,异常规格已被弃用,并推荐使用 noexcept 关键字来声明函数是否抛出异常。

异常规格(Exception Specification)用于声明函数可能抛出的异常类型。在函数声明中,可以通过在函数参数列表后面添加 throw(exception_type_list) 来指定函数可能抛出的异常类型。exception_type_list 是一个逗号分隔的异常类型列表。

如下为样例代码:

void func() throw(int, std::exception) 
{  // 函数实现  if (/* some condition */) {  throw std::runtime_error("An error occurred");  }  
}

这表示 func 函数可能抛出 int 类型或 std::exception 类型及其派生类型的异常。如果函数抛出了其他类型的异常,程序将调用 std::unexpected() 函数,除非该函数被 std::set_unexpected() 函数覆盖。

需要注意的是,基于类型的异常规格并不要求函数实际抛出列表中的异常类型,它只是一个声明,用于告知编译器和调用者该函数可能抛出的异常类型。

面试题 4 :说一下你对 noexcept 关键字的理解。

noexcept 是在 C++11 引入的关键字,它提供了一种更简洁且更强大的方式来指定函数是否抛出异常。noexcept关键字可以接受一个可选的常量表达式,用于指示函数是否可能抛出异常。

如下为样例代码:

void func1() noexcept {  // 函数实现,保证不抛出异常  
}  void func2() noexcept(true) {  // 函数实现,保证不抛出异常  
}  void func3() noexcept(false) {  // 函数实现,可能抛出异常  
}

使用 noexcept 关键字的好处是,如果函数声明为 noexcept 且实际上抛出了异常,编译器会保证调用 std::terminate() 来结束程序,而不是调用 std::unexpected()。此外,noexcept 还允许编译器进行更多的优化,因为它知道函数不会抛出异常。

因此,在现代 C++ 编程中,推荐使用 noexcept 关键字来替代 C++98 中的异常规格。

面试题 5 :如何重新抛出当前捕获的异常?

在C++中,如果在一个 catch 块中捕获了一个异常,并且想要在当前函数或更高级别的函数中重新抛出它,可以使用 throw; 语句(没有带任何参数)来重新抛出该异常。这允许异常继续向上传播,直到它被另一个 catch 块捕获或程序终止。

如下为样例代码:

#include <iostream>  
#include <stdexcept>  void handleException() 
{try {// ... 这里可能会抛出异常的代码 ...  throw std::runtime_error("An error occurred");}catch (const std::exception& e) {// 处理异常,例如记录日志  std::cerr << "Caught exception: " << e.what() << std::endl;// 重新抛出异常  throw;}
}int main() 
{try {handleException();}catch (const std::exception& e) {// 在主函数中捕获并处理异常  std::cerr << "Main function caught exception: " << e.what() << std::endl;return 1; // 返回一个错误代码  }return 0;
}

上面代码的输出为:

Caught exception: An error occurred
Main function caught exception: An error occurred

在上面代码中,handleException 函数捕获了一个 std::runtime_error 异常,并打印了一条错误消息。然后,它使用 throw; 语句重新抛出了这个异常,使得异常可以继续向上传播到 main 函数中的 catch 块。

注意:当重新抛出异常时,异常的类型和对象(如果是非基本类型)都会保持不变。这意味着在 main 函数中的 catch 块可以捕获到与 handleException 函数中相同的异常类型,并访问其 what() 方法来获取错误消息。

此外,如果想要捕获异常并对其进行处理,然后再抛出一个新的异常,可以使用 throw 语句后跟一个新的异常对象。这通常用于包装原始异常,并添加更多的上下文信息或转换异常类型。例如:

catch (const std::exception& e) {  // 处理原始异常  // ...  // 抛出一个新的异常,包含原始异常的信息  throw std::runtime_error("An error occurred while processing: " + e.what());  
}

面试题 6 :什么是栈展开(Stack Unwinding)?

在C++异常处理中,栈展开(Stack Unwinding)是一个过程,当异常被抛出时,程序会开始从当前执行点回溯(unwind)调用栈,以查找能够处理该异常的catch块。这个过程涉及销毁在抛出异常点之后创建的所有局部对象,释放它们所占用的资源。

栈展开的具体步骤如下:

(1)查找匹配的catch块: 当异常被抛出时,程序会开始从抛出异常的点回溯调用栈,检查每个函数调用的栈帧,看是否有 catch 块可以捕获该异常。这通常是通过检查每个函数的异常规格(如果使用了 C++98 的异常规格)或 catch 块参数类型来完成的。

(2)销毁局部对象: 在回溯过程中,对于每个栈帧,程序会销毁在抛出异常点之后创建的所有局部对象。这包括使用构造函数创建的对象,以及可能通过 new 表达式在栈上分配的对象。局部对象的析构函数会被调用,以释放它们所占用的资源。

(3)继续回溯: 如果当前栈帧中没有匹配的 catch 块,程序会继续回溯调用栈,重复步骤1和2,直到找到匹配的 catch 块或回溯到程序的入口点。

(4)处理异常: 一旦找到了匹配的 catch 块,程序将跳转到该 catch 块的代码,异常得到处理。此时,栈展开过程结束。

栈展开是 C++ 异常处理机制的一个重要组成部分,它确保了当异常发生时,程序能够正确地清理资源,并将控制权转移到能够处理该异常的代码。这也使得异常处理比传统的错误码处理方式更加安全和方便。

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

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

相关文章

Laravel Octane 和 Swoole 协程的使用分析

之前在工作中使用 Laravel Octane 的 concurrently 处理并发时&#xff0c;发现在队列和定时任务中不会触发并发效果。经过分析&#xff0c;作了如下猜测&#xff1a;队列和定时任务都属于一个独立的进程&#xff0c;与 Octane 服务无关&#xff0c;而 Octane concurrently 恰恰…

C#高级:DataGridView的详解

一、每条数据增加一个按钮&#xff0c;点击输出对应实体 请先确保正确添加实体的名称和文本&#xff1a; private void button6_Click(object sender, EventArgs e) {//SQL查询到数据&#xff0c;存于list中List<InforMessage> list bll.QueryInforMessage();//含有字段…

数据页和缓存页(BufferPool)

1. 数据页&#xff08;dataPage&#xff09; 什么是数据页&#xff1f; 数据页是 MySQL 存储引擎在磁盘和内存之间传输数据的基本单位&#xff0c;默认大小为16KB。 数据页的结构&#xff1a; 表头&#xff1a;储存与页相关的元信息&#xff0c;比如&#xff0c;页号&#…

buuctf_reverse_新年快乐+内涵的软件

新年快乐 题目&#xff1a;新年快乐.exe 这玩意有壳&#xff01;我去down了upx脱壳 开始放exeinfope的图片没截&#xff0c;我记得下载完upx后exeinfoPE显示还不一样。留了一张脱壳的实验图片&#xff1a; 然后放IDA 我是笔记本键盘&#xff0c;shiftFnF12转字符串 诶呦&…

flutter 安装流程

flutter文档 0.安装flutter https://flutter.cn/docs/get-started/install/windows/desktop?tabdownload 1.jdk 安装地址 Java Archive | Oracle 2. android studio 下载地址 https://developer.android.com/studio 3.Visual Studio下载地址 下载 Visual Studio Tools …

C++之map

1、map介绍 map是C STL的一个关联容器&#xff0c;它提供一对一的数据处理能力。其中&#xff0c;各个键值对的键和值可以是任意数据类型&#xff0c;包括 C 基本数据类型&#xff08;int、double 等&#xff09;、使用结构体或类自定义的类型。 第一个可以称为关键字(key)&…

docker构建hyperf环境

一&#xff0c;构建hyperf 镜像 官网git https://github.com/hyperf/hyperf-docker 使用dockerfile构建镜像 根据需要这里我使用8.1 swoole版本的镜像 在/home/hyperfdocker 目录中新建一个Dockerfile文件&#xff0c;将这个git上的Dockerfile内容复制粘贴进去 docker build…

HBase总结

基本介绍 特点(高可靠、高性能、面向列、可伸缩) 非关系型数据库大数据实时处理 表规模达数十亿行及数百万列读、写访问可实时分布式存储系统 HDFS(Hadoop Distributed File System)文件存储ZooKeeper作为协同服务列存储 优点:有利于单列数据查询缺点:整行读取时效率较慢,…

毫末智行开年融资,揭幕了自动驾驶最后的赛点

毫末智行日前官宣拿到的超亿元B1轮融资&#xff0c;在行业引起了不小的关注。 一方面是信心问题&#xff0c;自动驾驶从早期拼技术到去年拼量产落地&#xff0c;创业公司们的声量此消彼长&#xff0c;有人领先也有人掉队&#xff0c;但市场的态度都以谨慎为主&#xff0c;甚至…

Android ANR 日志分析定位

ANR 是 Android 应用程序中的 "Application Not Responding" 的缩写&#xff0c;中文意思是 "应用程序无响应"。这是当应用程序在 Android 系统上运行时&#xff0c;由于某种原因不能及时响应用户输入事件或执行一个操作&#xff0c;导致界面无法更新&…

Kubernetes(k8s第二部分)

资源清单相当于剧本 什么是资源&#xff1a; k8s中所有的内容都抽象为资源&#xff0c;资源实例化后&#xff0c;叫做对象。 1.K8S中的资源 集群资源分类 名称空间级别&#xff1a; kubeadm k8s kube-system kubectl get pod -n default 工作负载型资源&#xff0c;&a…

指针篇章-(1)

指针&#xff08;1&#xff09;学习流程 —————————————————————————————————————————————————————————————————————————————————————————————————————————————…

Linux:使用nslookup和dig查询DNS记录信息

nslookup nslookup&#xff08;Name Server Lookup&#xff09;用于从 DNS 服务器查询域名、IP 或其他 DNS 记录信息 示例 查询域名信息 $ nslookup baidu.comServer: 114.114.114.114 Address: 114.114.114.114#53Non-authoritative answer: Name: baidu.com Add…

springcloud项目,无法在Sentinel Dashboard查看到服务的访问监控信息【解决方法】

1.问题场景 因为warehouse子项目的前端未开发&#xff0c;所以只能通过postman测试接口访问&#xff1b; 2.解决方法 package org.sharetek.common.security.config;import cn.dev33.satoken.SaManager; import cn.dev33.satoken.filter.SaServletFilter; import cn.dev33.sat…

【Django】执行查询—检索对象(二)

主键 (pk) 查询快捷方式 pk查询等同于通过模型的 primary key 进行过滤&#xff0c;直接上例子&#xff0c;下面三个语句是等效的。 >>> Blog.objects.get(id__exact14) # Explicit form >>> Blog.objects.get(id14) # __exact is implied >>> …

构筑未来商业智能——数据中台的战略视角_光点科技

随着数字化时代的加速到来&#xff0c;企业对于数据的依赖程度日益增强。数据中台&#xff0c;作为连接数据与商业决策的关键枢纽&#xff0c;已成为企业打造持久竞争优势的战略选择。本文将从战略的高度&#xff0c;解析数据中台如何帮助企业捕捉数据红利&#xff0c;推进业务…

NXP实战笔记(十一):32K3xx基于RTD-SDK在S32DS上配置LPSPI(同步、异步、DMA、主机、从机、中断、轮询)

目录 1、概述 2、RTD-SDK配置 2.1、配置目标 2.2、主、从机引脚配置 2.3、时钟配置 2.4、LPSPI配置 2.5、中断配置 2.6、DMA配置(使用DMA才会配置) 2、dma Logic Instance 2.7、RM配置(使用DMA的情况下必须配置此选项) 3、代码实现 1、概述 S32K3_低功耗LPSPI轮询…

第七十六周周报

学习目标&#xff1a; 论文 实验 学习时间&#xff1a; 2024.2.24-2024.3.1 学习产出&#xff1a; 论文 仔细看了论文"All are Worth Words: A ViT Backbone for Diffusion Models"&#xff0c;”DiffiT: Diffusion Vision Transformers for Image Generation…

备战蓝桥杯Day19 - 堆排序基础知识

一、每日一题 - 填充 详细题解 s input() # 输入字符串 n len(s) # 定义字符的长度 judge ["00", "11", "0?", "1?", "?0", "?1", "??"] # 把所有的情况一一列举出来 count 0 # 设置计数…

性能优化篇(四) GPU Instancing

使用GPU Instancing可以在一个Draw Call中同时渲染多个相同或类似的物体&#xff0c;从而减少CPU和GPU的开销。 官方文档&#xff1a;https://docs.unity3d.com/Manual/GPUInstancing.html 启用GPU Instancing&#xff0c;我们可以选中一个材质&#xff0c;然后在Inspector窗口…