“现代C++ RAII库:设计、优化及实战应用“

文章目录

    • 代码
  • 示例
      • `raii` 类的使用
        • 示例代码:
        • 解释:
      • `raii_var` 类的使用
        • 示例代码:
        • 解释:
      • 结合线程使用
        • 示例代码:
        • 解释:
      • `make_raii` 函数
        • 签名:
        • 示例:
        • 解释:
      • `raii_bind_var` 函数
        • 签名:
        • 示例:
        • 解释:
      • 总结

代码

/** raii.h**/#ifndef COMMON_SOURCE_CPP_RAII_H_
#define COMMON_SOURCE_CPP_RAII_H_
#include <type_traits>
#include <functional>
#include <utility>
#include <cassert>
namespace gdface {/** RAII方式管理申请和释放资源的类* 对象创建时,执行acquire(申请资源)动作(可以为空函数[]{})* 对象析构时,执行release(释放资源)动作* 禁止对象拷贝和赋值*/class raii {public:using fun_type = std::function<void()>;/* release: 析构时执行的函数* acquire: 构造函数执行的函数* default_com:_commit,默认值,可以通过commit()函数重新设置*/explicit raii(fun_type release, fun_type acquire = [] {}, bool default_com = true) :_commit(default_com), _release(release) {acquire();}/* 对象析构时根据_commit标志执行_release函数 */~raii() noexcept {if (_commit)_release();}/* 移动构造函数 允许右值赋值 */raii(raii&& rv)noexcept :_commit(rv._commit), _release(std::move(rv._release)) {rv._commit = false;};/* 禁用拷贝构造函数 */raii(const raii&) = delete;/* 禁用赋值操作符 */raii& operator=(const raii&) = delete;/* 设置_commit标志 */raii& commit(bool c = true)noexcept { _commit = c; return *this; };private:/* 为true时析构函数执行_release */bool _commit;protected:/* 析构时执的行函数 */std::function<void()> _release;}; /* raii *//* 用于实体资源的raii管理类* T为资源类型* acquire为申请资源动作,返回资源T* release为释放资源动作,释放资源T*/template<typename T>class raii_var {public:using    _Self = raii_var<T>;using   resource_type = T;using	acq_type = std::function<T()>;using	rel_type = std::function<void(T&)>;explicit raii_var(acq_type acquire, rel_type release) noexcept :_resource(acquire()), _do_release(release) {//构造函数中执行申请资源的动作acquire()并初始化resource;}/** 对于有默认构造函数的类型提供默认构造函数 */template<typename _T = T, typename Enable = typename std::enable_if<std::is_default_constructible<_T>::value>::type>raii_var()noexcept :_resource(), _need_release(false) {}/* 对于有移动构造函数的类型提供移动构造函数 */template<class _T = T>raii_var(typename std::enable_if< std::is_object<_T>::value&& !std::is_default_constructible<_T>::value&& std::is_move_constructible<_T>::value, raii_var>::type&& rv): _resource(std::move(rv._resource)), _do_release(rv._do_release){rv._need_release = false;//控制右值对象析构时不再执行_release}/* 对于只有复制构造函数的类型提供移动构造函数 */template<class _T = T>raii_var(typename std::enable_if<   std::is_object<_T>::value&& !std::is_default_constructible<_T>::value&& !std::is_move_constructible<_T>::value&& std::is_copy_constructible<_T>::value, raii_var>::type&& rv): _resource(rv._resource), _do_release(rv._do_release){rv._need_release = false;//控制右值对象析构时不再执行_release}/* 对于指针和引用类型提供移动构造函数 */template<class _T = T>raii_var(typename std::enable_if< std::is_reference<_T>::value || std::is_pointer<_T>::value, raii_var>::type&& rv): _resource(rv._resource), _do_release(rv._do_release){rv._need_release = false;//控制右值对象析构时不再执行_release}/* 对于有复制构造函数的类型提供移动赋值操作符(这个操作符似乎没什么用,考虑删除) */template<typename _T = T, typename Enable = typename std::enable_if<std::is_copy_constructible<_T>::value>::type>raii_var& operator=(raii_var&& rv) {// 与右值对象(rv)交换所有成员变量,// rv在析构的时候会根据_need_release标志正确释放当前对象原有的资源std::swap(rv._resource, this->_resource);std::swap(rv._do_release, this->_do_release);std::swap(rv._need_release, this->_need_release);return *this;}/* 对象析构时根据_commit标志执行_release函数 */~raii_var() noexcept {if (_need_release)_do_release(_resource);}/* 设置_need_release标志 */_Self& release(bool rel = true)noexcept { _need_release = rel; return *this; };/* 设置_need_release标志为false,析构时不执行_release */_Self& norelease()noexcept { return release(false); };/* 获取资源引用 */T& get() noexcept { return _resource; }const T& get() const noexcept { return _resource; }T& operator*() noexcept { return get(); }const T& operator*() const noexcept { return get(); }/* 标量类型提供()操作符 */template<typename _T = T, typename Enable = typename std::enable_if<std::is_scalar<_T>::value>::type>operator T () const noexcept { return _resource; }/* 根据 T类型不同选择不同的->操作符模板 */template<typename _T = T>typename std::enable_if<std::is_pointer<_T>::value, _T>::type operator->()  noexcept{return _resource;}template<typename _T = T>typename std::enable_if<std::is_pointer<_T>::value, const _T>::type operator->() const noexcept{return _resource;}template<typename _T = T>typename std::enable_if<std::is_class<_T>::value, const _T*>::type operator->() const noexcept{return std::addressof(_resource);}template<typename _T = T>typename std::enable_if<std::is_class<_T>::value, _T*>::type operator->() noexcept{return std::addressof(_resource);}template<typename _T>typename std::enable_if<!std::is_same<_T, T>::value&& std::is_class<_T>::value, _T&>::type _get() noexcept{return static_cast<_T&>(_resource);}template<typename _T>typename std::enable_if<!std::is_same<_T, T>::value&& std::is_pointer<_T>::value, _T>::type _get() noexcept{return static_cast<_T>(_resource);}private:/* 为true时析构函数执行release */bool	_need_release = true;T	_resource;rel_type _do_release;};/* 创建 raii 对象,* 用std::bind将M_REL,M_ACQ封装成std::function<void()>创建raii对象* RES		资源类型* M_REL	释放资源的成员函数地址* M_ACQ	申请资源的成员函数地址*/template<typename RES, typename M_REL, typename M_ACQ>inline raii make_raii(RES& res, M_REL rel, M_ACQ acq, bool default_com = true) {// 编译时检查参数类型// 静态断言中用到的is_class,is_member_function_pointer等是用于编译期的计算、查询、判断、转换的type_traits类,// 有点类似于java的反射(reflect)提供的功能,不过只能用于编译期,不能用于运行时。// 关于type_traits的详细内容参见:http://www.cplusplus.com/reference/type_traits/static_assert(std::is_class<RES>::value, "RES is not a class or struct type.");static_assert(std::is_member_function_pointer<M_REL>::value, "M_REL is not a member function.");static_assert(std::is_member_function_pointer<M_ACQ>::value, "M_ACQ is not a member function.");assert(nullptr != rel && nullptr != acq);auto p_res = std::addressof(const_cast<typename std::remove_const<RES>::type&>(res));return raii(std::bind(rel, p_res), std::bind(acq, p_res), default_com);}/* 创建 raii 对象 无需M_ACQ的简化版本 */template<typename RES, typename M_REL>inline raii make_raii(RES& res, M_REL rel, bool default_com = true) {static_assert(std::is_class<RES>::value, "RES is not a class or struct type.");static_assert(std::is_member_function_pointer<M_REL>::value, "M_REL is not a member function.");assert(nullptr != rel);auto p_res = std::addressof(const_cast<typename std::remove_const<RES>::type&>(res));return raii(std::bind(rel, p_res), [] {}, default_com);}/* raii方式管理F(Args...)函数生产的对象* 如果调用时指定T类型,则返回的RAII对象类型为T,否则类型为F(Args...)结果类型*/template<typename T = void, typename F, typename... Args,typename ACQ_RES_TYPE = typename std::result_of<F(Args...)>::type,typename TYPE = typename std::conditional<!std::is_void<T>::value && !std::is_same<T, ACQ_RES_TYPE>::value, T, ACQ_RES_TYPE>::type,typename REL = std::function<void(TYPE&)> >inline raii_var<TYPE>raii_bind_var(REL rel, F&& f, Args&&... args) {return raii_var<TYPE>([&]()->TYPE {return static_cast<TYPE>(std::bind(std::forward<F>(f), std::forward<Args>(args)...)()); },rel);}
} /* namespace gdface */
#endif /* COMMON_SOURCE_CPP_RAII_H_ */

示例

理解如何使用raiiraii_var类的关键在于了解它们是如何帮助你管理资源的生命周期的。让我们通过具体的例子来详细解释这两个类的使用方法。

raii 类的使用

假设我们需要在程序中打开一个文件,并确保无论程序执行过程中发生什么,文件都能被正确关闭。我们可以使用raii类来管理这个过程。

示例代码:
#include <fstream>
#include <iostream>
#include "raii.h" // 假设你的RAII头文件名为raii.husing namespace gdface;void writeToFile(std::ofstream* file) {if (file->is_open()) {*file << "Hello, RAII!" << std::endl;}
}int main() {std::ofstream* pFileStream = nullptr;auto acquire = [&]() { pFileStream = new std::ofstream("example.txt", std::ios::out); };auto release = [&]() {if (pFileStream != nullptr) {pFileStream->close();delete pFileStream;pFileStream = nullptr;}};raii fileGuard(release, acquire);writeToFile(pFileStream);return 0;
}
解释:
  1. acquire 函数:负责打开文件并分配内存。
  2. release 函数:负责关闭文件并释放内存。
  3. raii 对象 fileGuard:在其构造时调用acquire打开文件,在析构时自动调用release关闭文件。
  4. writeToFile 函数:使用已经打开的文件进行写操作。

raii_var 类的使用

raii_var 更加通用,可以直接持有资源类型(如文件流),并且可以方便地进行访问和操作。

示例代码:

假设我们同样需要管理一个文件流资源,但这次直接使用raii_var来简化代码。

#include <fstream>
#include <iostream>
#include "raii.h" // 假设你的RAII头文件名为raii.husing namespace gdface;void writeToFile(raii_var<std::ofstream>& fileGuard) {if (fileGuard->is_open()) {(*fileGuard) << "Hello, RAII_var!" << std::endl;}
}int main() {auto acquire = []() -> std::ofstream {return std::ofstream("example.txt", std::ios::out);};auto release = [](std::ofstream& stream) {if (stream.is_open()) {stream.close();}};raii_var<std::ofstream> fileGuard(acquire, release);writeToFile(fileGuard);return 0;
}
解释:
  1. acquire 函数:返回一个新的文件流对象。
  2. release 函数:接受一个文件流引用,并在其上调用close()方法。
  3. raii_var 对象 fileGuard:在其构造时调用acquire创建文件流,在析构时自动调用release关闭文件流。
  4. writeToFile 函数:使用fileGuard提供的文件流进行写操作。

结合线程使用

如果你希望在多线程环境中使用这些RAII类来管理资源,可以通过将RAII对象传递给线程函数来实现。

示例代码:
#include <fstream>
#include <iostream>
#include <thread>
#include "raii.h" // 假设你的RAII头文件名为raii.husing namespace gdface;void threadFunc(raii_var<std::ofstream>& fileGuard) {if (fileGuard->is_open()) {(*fileGuard) << "Hello from Thread using RAII_var!" << std::endl;}
}int main() {auto acquire = []() -> std::ofstream {return std::ofstream("example.txt", std::ios::out);};auto release = [](std::ofstream& stream) {if (stream.is_open()) {stream.close();}};raii_var<std::ofstream> fileGuard(acquire, release);std::thread t(threadFunc, std::ref(fileGuard));t.join(); // 等待线程完成return 0;
}
解释:
  1. std::thread:启动一个新线程并传递fileGuard的引用。
  2. std::ref(fileGuard):确保传递的是fileGuard的引用而不是副本,这样线程可以共享同一个RAII对象。
  3. t.join():等待线程完成,确保主线程在子线程结束前不会退出。

通过这种方式,你可以确保无论是在单线程还是多线程环境下,资源都会被正确地获取和释放,避免了资源泄露的风险。

确实,代码中还定义了两个辅助函数 make_raiiraii_bind_var,它们简化了创建 raiiraii_var 对象的过程。让我们详细解释这些辅助函数的用途和使用方法。

make_raii 函数

make_raii 是一个模板函数,用于简化 raii 对象的创建过程。它通过绑定类成员函数来自动创建 std::function<void()> 类型的资源获取和释放函数。

签名:
template<typename RES, typename M_REL, typename M_ACQ>
inline raii make_raii(RES & res, M_REL rel, M_ACQ acq, bool default_com = true);
  • 参数:
    • RES: 资源类型。
    • M_REL: 释放资源的成员函数指针。
    • M_ACQ: 获取资源的成员函数指针。
    • res: 资源对象的引用。
    • default_com: 是否在析构时执行释放操作,默认为 true
示例:

假设我们有一个类 ResourceManager,其中包含打开和关闭文件的方法。

#include <fstream>
#include <iostream>
#include "raii.h" // 假设你的RAII头文件名为raii.husing namespace gdface;class ResourceManager {
public:void openFile(const std::string& filename) {file.open(filename, std::ios::out);}void closeFile() {if (file.is_open()) {file.close();}}private:std::ofstream file;
};void writeToFile(ResourceManager& manager, const std::string& message) {manager.openFile("example.txt");if (manager.file.is_open()) {manager.file << message << std::endl;}
}int main() {ResourceManager manager;auto guard = make_raii(manager, &ResourceManager::closeFile, &ResourceManager::openFile);writeToFile(manager, "Hello from RAII!");return 0;
}
解释:
  1. make_raii: 创建了一个 raii 对象 guard,它会调用 manageropenFile 方法获取资源,并在析构时调用 closeFile 方法释放资源。
  2. writeToFile: 使用 manager 对象进行写操作,确保文件在 main 函数结束时会被正确关闭。

raii_bind_var 函数

raii_bind_var 是另一个模板函数,用于简化 raii_var 对象的创建。它可以绑定任意函数及其参数,并生成相应的资源管理对象。

签名:
template<typename T=void,typename F, typename... Args,typename ACQ_RES_TYPE=typename std::result_of<F(Args...)>::type,typename TYPE=typename std::conditional<!std::is_void<T>::value&&!std::is_same<T,ACQ_RES_TYPE>::value,T,ACQ_RES_TYPE>::type,typename REL=std::function<void(TYPE&)> >
inline raii_var<TYPE>
raii_bind_var(REL rel,F&& f, Args&&... args);
  • 参数:
    • T: 可选的资源类型,如果未指定,则默认为 F(Args...) 的返回类型。
    • F: 资源获取函数。
    • Args: 资源获取函数的参数。
    • rel: 资源释放函数。
示例:

假设我们有一个简单的函数来创建一个文件流,并希望使用 raii_var 来管理这个文件流。

#include <fstream>
#include <iostream>
#include "raii.h" // 假设你的RAII头文件名为raii.husing namespace gdface;std::ofstream createFileStream(const std::string& filename) {return std::ofstream(filename, std::ios::out);
}void releaseFileStream(std::ofstream& stream) {if (stream.is_open()) {stream.close();}
}void writeToFile(std::ofstream& file, const std::string& message) {if (file.is_open()) {file << message << std::endl;}
}int main() {auto fileGuard = raii_bind_var<std::ofstream>(releaseFileStream,createFileStream,"example.txt");writeToFile(fileGuard.get(), "Hello from RAII_var!");return 0;
}
解释:
  1. createFileStream: 创建并返回一个新的 std::ofstream 对象。
  2. releaseFileStream: 接受一个 std::ofstream 引用,并在其上调用 close() 方法。
  3. raii_bind_var: 创建了一个 raii_var<std::ofstream> 对象 fileGuard,它会在构造时调用 createFileStream 获取文件流,在析构时调用 releaseFileStream 释放文件流。
  4. writeToFile: 使用 fileGuard 提供的文件流进行写操作。

总结

  • make_raii: 简化了 raii 对象的创建,适用于需要通过类成员函数管理资源的情况。
  • raii_bind_var: 简化了 raii_var 对象的创建,适用于需要通过普通函数或可调用对象管理资源的情况。

这两个辅助函数的主要目的是减少样板代码,使资源管理更加简洁和安全。通过它们,你可以更方便地利用 RAII 模式来确保资源的正确获取和释放,无论是在单线程还是多线程环境中。

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

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

相关文章

代码随想录day06

242.有效的字母异位词 刚学哈希表想着使用unordered_set来实现&#xff0c;结果无法通过&#xff0c;原因是对字母异位词理解有问题&#xff0c;字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语&#xff0c;并使用所有原字母一次。对字母出现的次数有要求&am…

Java_双列集合

双列集合特点 存放的是键值对对象&#xff08;Entry&#xff09; Map 因为都是继承Map&#xff0c;所以要学会这些API&#xff0c;后面的类就都知道了 put 有两个操作&#xff0c;添加&#xff08;并返回null&#xff09;或者覆盖&#xff08;返回被覆盖的值&#xff09…

MHTML文件如何在前端页面展示

MHTML文件如何在前端页面展示 需求背景&#xff1a; 目前在给证券公司做项目&#xff0c;但是在使用新系统的过程中&#xff0c;甲方还希望之前之前系统的历史记录可以看到。 最初制定的计划是项目组里面做数据的把原系统页面爬取下来&#xff0c;转成图片&#xff0c;直接给…

解决bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException

解决Spring Boot中MySQL数据库报错“Bad SQL Grammar”的问题 目录 解决Spring Boot中MySQL数据库报错“Bad SQL Grammar”的问题 问题描述解决步骤解决方案结论附&#xff1a;MySql常用配置参数及使用场景 在使用Spring Boot连接MySQL数据库时&#xff0c;有时候会遇到“B…

【AI日记】25.02.08

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】【AI应用】 探索 AI 应用探索周二有个面试&#xff0c;明后天打算好好准备一下&#xff0c;我打算主要研究下 AI 如何在该行业赋能和应用&#xff0c;以及该行业未来的发展前景和公司痛点&#…

【AI学习】关于 DeepSeek-R1的几个流程图

遇见关于DeepSeek-R1的几个流程图&#xff0c;清晰易懂形象直观&#xff0c;记录于此。 流程图一 来自文章《Understanding Reasoning LLMs》&#xff0c; 文章链接&#xff1a;https://magazine.sebastianraschka.com/p/understanding-reasoning-llms?continueFlagaf07b1a0…

零基础都可以本地部署Deepseek R1

文章目录 一、硬件配置需求二、详细部署步骤1. 安装 Ollama 工具2. 部署 DeepSeek-R1 模型3. API使用4. 配置图形化交互界面&#xff08;可选&#xff09;5. 使用与注意事项 一、硬件配置需求 不同版本的 DeepSeek-R1 模型参数量不同&#xff0c;对硬件资源的要求也不尽相同。…

索引失效的场景

chatGpt 7. 使用 DISTINCT 或 GROUP BY 当查询中涉及 DISTINCT 或 GROUP BY 时&#xff0c;如果查询没有合适的索引支持&#xff0c;可能会导致性能问题&#xff0c;虽然不完全是索引失效&#xff0c;但会影响查询效率。 sql SELECT DISTINCT department_id FROM employees;…

Node.js中http模块(二)

一、http模块 http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供的 http.createServer0) 方法&#xff0c;就能方便的把一台普通的电脑&#xff0c;变成一台 Web 服务器&#xff0c;从而对外提供 Web 资源服务。 二、域名和域名服务器 尽管 I…

Dockerfile 文件详解

在平常的开发工作中&#xff0c;我们经常需要部署项目&#xff0c;一个项目开发完成后&#xff0c;使用 Docker 方式部署&#xff0c;那么首先得构造镜像&#xff0c;构造镜像最主要的就是 Dockerfile 文件的编写&#xff0c;今天简单来总结下 Dockerfile 文件的编写以及有哪些…

Git提交错误解决:missing Change-Id in message footer

问题现象&#xff1a; 提交的commit中没有插入change id导致push代码失败。 问题解决&#xff1a; 针对该错误&#xff0c;Git已经给出了解决方案&#xff1a; 1、to automatically insert a Change-Id, install the hook: gitdir$(git rev-parse --git-dir); scp -p -P 2…

第四十一章:沪上逐梦:适应新环境的挑战

2022 年底&#xff0c;凛冽的寒风裹挟着岁末的清冷&#xff0c;毫无保留地席卷了整个华夏大地。上海&#xff0c;这座屹立在东方的繁华都市&#xff0c;在这冰寒彻骨的冬日里&#xff0c;愈发显得冷峻而深沉。小冷怀揣着对新工作的满心期待&#xff0c;以及对未来的无限憧憬&am…

多租户架构设计与实现:基于 PostgreSQL 和 Node.js

多租户架构设计与实现:基于 PostgreSQL 和 Node.js 引言 多租户架构(Multi-tenancy)是现代 SaaS(Software as a Service)应用的核心设计模式之一。它允许多个租户共享同一套应用实例,同时确保数据隔离和安全性。本文将详细介绍多租户架构的设计方案,并基于 PostgreSQL…

顺丰java面试题_顺丰java开发面试分享,顺丰java面试经面试题

今天要给大家分享的是一个小伙伴的顺丰java开发面试过程&#xff0c;其中包括了面试流程&#xff0c;面试题目&#xff0c;和回答&#xff0c;感兴趣的朋友可以来了解一下哈。 一、面试流程 是中午进行的面试&#xff0c;首先是做自我介绍&#xff0c;之后就是讲一下项目&…

天津三石峰科技——汽车生产厂的设备振动检测项目案例

汽车产线有很多传动设备需要长期在线运行&#xff0c;会出现老化、疲劳、磨损等 问题&#xff0c;为了避免意外停机造成损失&#xff0c;需要加装一些健康监测设备&#xff0c;监测设备运 行状态。天津三石峰科技采用 12 通道振动信号采集卡&#xff08;下图 1&#xff09;对…

SpringBoot教程(十四) SpringBoot之集成Redis

SpringBoot教程&#xff08;十四&#xff09; | SpringBoot之集成Redis 一、Redis集成简介二、集成步骤 2.1 添加依赖2.2 添加配置2.3 项目中使用之简单使用 &#xff08;举例讲解&#xff09;2.4 项目中使用之工具类封装 &#xff08;正式用这个&#xff09;2.5 序列化 &…

多智能体协作架构模式:驱动传统公司向AI智能公司转型

前言 在数字化浪潮的席卷下&#xff0c;传统公司的运营模式正面临着前所未有的挑战。随着市场竞争的日益激烈&#xff0c;客户需求的快速变化以及业务复杂度的不断攀升&#xff0c;传统公司在缺乏 AI 技术支撑的情况下&#xff0c;暴露出诸多痛点。在决策层面&#xff0c;由于…

rBits.exe服务备份

Program.cs using System; using System.Collections.Generic; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks;namespace rBits {internal static class Program{/// <summary>/// 应用程序的主入口点。/// </s…

边缘计算网关驱动智慧煤矿智能升级——实时预警、低延时决策与数字孪生护航矿山安全高效运营

迈向智能化煤矿管理新时代 工业物联网和边缘计算技术的迅猛发展&#xff0c;煤矿安全生产与高效运营正迎来全新变革。传统煤矿监控模式由于现场环境复杂、数据采集和传输延时较高&#xff0c;已难以满足当下高标准的安全管理要求。为此&#xff0c;借助边缘计算网关的实时数据…

互联网分布式ID解决方案

业界实现方案 1. 基于UUID 2. 基于DB数据库多种模式(自增主键、segment) 3. 基于Redis 4. 基于ZK、ETCD 5. 基于SnowFlake 6. 美团Leaf(DB-Segment、zkSnowFlake) 7. 百度uid-generator() 基于UUID生成唯一ID UUID生成策略 推荐阅读 DDD领域驱动与微服务架构设计设计模…