异常处理、文件操作和智能指针

news/2025/3/22 15:29:01/文章来源:https://www.cnblogs.com/barry-first/p/18786615

好的!让我们深入探讨 C++ 中三个至关重要的核心主题:异常处理文件操作智能指针。我将以工业级代码标准,结合底层原理和实战案例,带你彻底掌握这些技术。


第一部分:异常处理(Exception Handling)

1. 异常机制的核心价值

传统错误处理的缺陷

bool readFile(const char* path, char* buffer, size_t size) {FILE* file = fopen(path, "r");if (!file) return false; // 错误码层层传递if (fread(buffer, 1, size, file) != size) {fclose(file); // 容易忘记关闭return false;}fclose(file);return true;
}

问题

  • 错误信息传递困难
  • 资源释放容易遗漏
  • 多层嵌套代码难以维护

2. C++ 异常处理三要素

(1) 基本语法

try {// 可能抛出异常的代码if (error_condition) {throw std::runtime_error("Error message");}
} 
catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << "\n";
} 
catch (...) {// 捕获所有未处理的异常
}

(2) 标准异常体系

std::exception  
├── logic_error
│   ├── invalid_argument
│   ├── domain_error
│   ├── length_error
│   ├── future_error (C++11)
│   └── bad_optional_access (C++17)
│  
├── runtime_error
│   ├── range_error
│   ├── overflow_error 
│   ├── underflow_error
│   ├── system_error (C++11)
│   └── bad_variant_access (C++17)
│  
├── bad_alloc 
├── bad_cast
├── bad_typeid
├── bad_exception
├── bad_function_call (C++11) 
├── bad_weak_ptr (C++11)
├── bad_array_new_length (C++11)
└── ...(其他特定异常)  

(3) 异常安全保证等级

等级 描述
基本保证 不泄露资源,对象处于有效状态
强保证 操作要么完全成功,要么保持原有状态(类似事务)
无抛出保证 承诺不抛出任何异常(移动操作、析构函数应尽量做到)

3. RAII 与异常安全的完美结合

class DatabaseConnection {sqlite3* db;
public:explicit DatabaseConnection(const std::string& path) {if (sqlite3_open(path.c_str(), &db) != SQLITE_OK) {throw std::runtime_error("Failed to open database");}}~DatabaseConnection() noexcept {if (db) sqlite3_close(db);}// 禁用拷贝(避免双释放)DatabaseConnection(const DatabaseConnection&) = delete;DatabaseConnection& operator=(const DatabaseConnection&) = delete;// 启用移动语义DatabaseConnection(DatabaseConnection&& other) noexcept : db(other.db) { other.db = nullptr; }
};

使用示例

try {DatabaseConnection conn("data.db");// 执行数据库操作...
} 
catch (const std::exception& e) {// 即使发生异常,conn 析构仍会正确关闭连接
}

第二部分:文件操作(File I/O)

1. 标准库文件流体系

功能描述
std::ifstream 输入文件流(读操作)
std::ofstream 输出文件流(写操作)
std::fstream 双向文件流(读写操作)

2. 文件操作最佳实践

(1) 安全打开文件

#include <fstream>
#include <system_error> // 包含 std::io_errcvoid readFile(const std::string& path) {std::ifstream file(path, std::ios::binary);// 显式检查打开状态if (!file.is_open()) {throw std::system_error(std::make_error_code(std::io_errc::stream),"Failed to open file");}// 设置异常掩码(文件错误时抛出异常)file.exceptions(std::ios::failbit | std::ios::badbit);try {// 读取文件内容...} catch (const std::ios_base::failure& e) {// 处理具体 I/O 错误throw std::runtime_error("File read error: " + std::string(e.what()));}
}

(2) 二进制文件操作

struct Pixel {uint8_t r, g, b;
};void writeImage(const std::string& path, const Pixel* data, size_t count) {std::ofstream file(path, std::ios::binary);if (!file) throw std::runtime_error("Open failed");// 直接写入二进制数据file.write(reinterpret_cast<const char*>(data), count * sizeof(Pixel));// 显式检查写入是否完整if (file.bad()) {throw std::runtime_error("Write failed");}
}

第三部分:智能指针(Smart Pointers)

1. 智能指针进化史

指针类型 C++ 标准 所有权模型 特点
auto_ptr C++98 转移所有权 已废弃,存在安全隐患
unique_ptr C++11 独占所有权 轻量、高效、支持自定义删除器
shared_ptr C++11 共享所有权 引用计数、线程安全
weak_ptr C++11 观测共享对象 解决循环引用问题

2. unique_ptr 深度解析

(1) 基础用法

#include <memory>void processData() {// 创建独占指针std::unique_ptr<int[]> buffer = std::make_unique<int[]>(1024);// 自动管理内存buffer[0] = 42;// 转移所有权(移动语义)std::unique_ptr<int[]> new_owner = std::move(buffer);// 此时 buffer 为空指针
}

(2) 自定义删除器

// 用于 C 风格文件指针
struct FileDeleter {void operator()(FILE* fp) const {if (fp) fclose(fp);}
};void useFile() {std::unique_ptr<FILE, FileDeleter> file(fopen("data.bin", "rb"));if (!file) throw std::runtime_error("Open failed");// 自动调用 FileDeleter 关闭文件
}

3. shared_ptrweak_ptr 的协同

(1) 共享所有权示例

class TreeNode {
public:std::shared_ptr<TreeNode> parent;std::vector<std::shared_ptr<TreeNode>> children;~TreeNode() { std::cout << "TreeNode destroyed\n"; }
};void createTree() {auto root = std::make_shared<TreeNode>();auto child = std::make_shared<TreeNode>();child->parent = root;      // 共享所有权root->children.push_back(child);// 离开作用域时自动释放
}

(2) 循环引用问题解决方案

class Device;
class Controller;class Device {
public:std::weak_ptr<Controller> ctrl; // 使用 weak_ptr 打破循环~Device() { std::cout << "Device destroyed\n"; }
};class Controller {
public:std::shared_ptr<Device> device;~Controller() { std::cout << "Controller destroyed\n"; }
};void createSystem() {auto ctrl = std::make_shared<Controller>();auto dev = std::make_shared<Device>();ctrl->device = dev;dev->ctrl = ctrl; // 不会增加引用计数// 离开作用域时能正确析构
}

第四部分:综合实战——安全文件处理器

#include <iostream>
#include <fstream>
#include <memory>
#include <vector>class FileProcessor {std::unique_ptr<std::vector<char>, void(*)(std::vector<char>*)> buffer;public:explicit FileProcessor(const std::string& path) : buffer(nullptr, [](auto p){ if(p) delete p; }) {std::ifstream file(path, std::ios::binary | std::ios::ate);if (!file) throw std::runtime_error("Cannot open file");const auto size = file.tellg();file.seekg(0);auto* data = new std::vector<char>(size);if (!file.read(data->data(), size)) {delete data;throw std::runtime_error("Read failed");}buffer.reset(data);}void process() {if (!buffer) throw std::logic_error("No data loaded");// 处理缓冲区数据...}void saveTo(const std::string& path) {std::ofstream out(path, std::ios::binary);if (!out) throw std::runtime_error("Cannot create output file");if (!out.write(buffer->data(), buffer->size())) {throw std::runtime_error("Write failed");}}
};int main() {try {FileProcessor processor("input.dat");processor.process();processor.saveTo("output.dat");} catch (const std::exception& e) {std::cerr << "Fatal error: " << e.what() << "\n";return EXIT_FAILURE;}return EXIT_SUCCESS;
}

关键知识点总结表

主题 核心要点 最佳实践
异常处理 - 使用 RAII 保证异常安全
- 优先使用标准异常类型
- 避免在析构函数抛异常
- 为自定义异常继承 std::exception
- 保持异常消息简明
文件操作 - 检查文件打开状态
- 显式指定二进制模式
- 使用 RAII 管理文件句柄
- 用 std::filesystem 处理路径(C++17)
- 大文件使用内存映射
智能指针 - 优先使用 unique_ptr
- shared_ptr 用于共享所有权
- weak_ptr 解决循环引用
- 使用 make_shared/make_unique
- 避免裸指针与智能指针混用

通过这个系统化的讲解,你应该已经掌握:

  1. 如何构建异常安全的健壮代码
  2. 高效可靠的文件操作方法
  3. 智能指针的正确使用场景

下一步建议:

  1. 使用 AddressSanitizer 检测内存错误
  2. 研究标准库源码(如 std::shared_ptr 的引用计数实现)
  3. 实践复杂资源管理场景(如数据库连接池)

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

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

相关文章

异常的两种处理方式--java进阶day08

1.异常的默认处理流程 java中,对于异常的默认处理方式是--向上抛出 之前我们说过,异常都是类,当某个程序出错后,就会自动生成该异常对象,而这个异常对象就如同一颗雷.java的异常默认处理方式--向上抛出,其中上,就是指上一级,调用该程序的那个逻辑 所以,在错误代码那一…

在centOS上更新yum资源报错:Cannot find a valid baseurl for repo: base/7/x86_64

在centOS中更新yum 的资源和新的依赖时,报:Cannot find a valid baseurl for repo: base/7/x86_64 报错内容:[root@localhost ~]# yum update 已加载插件:fastestmirror Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo…

并行计算架构和编程 | Assignment 1: Performance Analysis on a Quad-Core CPU

from pixivvAssignment 1: Performance Analysis on a Quad-Core CPU Environment SetupCPU信息 Architecture: x86_64CPU op-mode(s): 32-bit, 64-bitAddress sizes: 46 bits physical, 57 bits virtualByte Order: Little Endian C…

Contest3923 - 计科23级算法设计与分析上机作业-03

A.质数 题面思路 考虑到输入数据量较大,选择线性欧拉筛预处理 示例代码 #include<bits/stdc++.h>using namespace std;#define ll long long //#define int ll #define pii pair<int, int> #define all(x) x.begin(),x.end() #define fer(i, m, n) for(int i = m;…

leetcode 4. 两个有序数组的中位数(第k大的数)

假设有前 k 小的数,分配到两个数组中综上, 前k-1数的边界偏离(k-1)/2 时,由于大于(k-1)数边界的挤压会伴随小于k的数的边界的外延, 其在(k-1)/2会呈现一方比另一方大的情况,可以直接判定小的一方在小于k的数的边界内 而当k-1数正好在边界内,则同样可以判定小的数在小于k的…

20241227曹鹏泰 python1

课程:《Python 程序设计》 班级: 2412 姓名: 曹鹏泰 学号: 20241227 实验教师:王志强 实验日期:2025 年 3 月 12 日 必修/选修: 公选课 一、实验内容 熟悉 Python 开发环境; 练习 Python 运行、调试技能(编写书中的程序,并进行调试分析,要有过程); 编写程序…

ospfv3收到adv为全零的5类lsa,该怎么处理?

问题现象:ospfv3建立邻居后发现部分路由丢失原因:抓包查看时发现对端华为设备发送的5类LSA报文中ADV为全0,设备将LSA加到LSDB后,未将其加到边界路由表,导致下发路由中缺失部分路由 临时处理办法:下发边界路由时检查LSDB中是否存在ADV为全0的5类LSA,存在则查找LSDB,得到…

FristiLeaks_1

FristiLeaks_1.3 环境搭建 下载:https://download.vulnhub.com/fristileaks/FristiLeaks_1.3.ova 导入后将mac地址修改为:08:00:27:A5:A6:76信息收集 扫描主机ip ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:84:b2:cc, IPv4: 1…

绿联nas防火墙导致docker网络无法互通

问题描述设备:绿联nas dxp4800 系统:ugnas pro 绿联新系统在12月份更新后,原本用nginx代理的alist,青龙等服务全都连接不上,在ugnas系统防火墙设置如下:对外只通过80端口,其他docker服务都只能通过nginx反代访问,系统更新前一直都没问题。 问题排查 经过反复排查发现关…

20244119 实验一 《Python程序设计》 实验报告

课程:《Python程序设计》 班级: 2441 姓名: 霍彬斌 学号:20244109 实验教师:王志强 必修/选修: 公选课 一、实验内容 熟悉Pycharm等开发环境; 掌握基本的Python运行和调试技能; 掌握基本的Python编程技能。 二、实验过程及结果 1.熟悉Python开发环境; 本次实验使用pyc…

一步一步教你部署ktransformers,大内存单显卡用上Deepseek-R1

环境准备 硬件环境 CPU:intel四代至强及以上,AMD参考同时期产品 内存:800GB以上,内存性能越强越好,建议DDR5起步 显卡:Nvidia显卡,单卡显存至少24GB(用T4-16GB显卡实测会在加载模型过程中爆显存),nvidia compute capability至少8.0(CUDA GPUs - Compute Capability …

20244119实验一《Python程序设计》实验报告

课程:《Python程序设计》 班级: 2441 姓名: 霍彬斌 学号:20244109 实验教师:王志强 必修/选修: 公选课 一、实验内容 熟悉Pycharm等开发环境; 掌握基本的Python运行和调试技能; 掌握基本的Python编程技能。 二、实验过程及结果 1.熟悉Python开发环境; 本次实验使用pyc…