编写高效的C++程序方法之使用对象池

2023-11-03

  

        对象池技术可以避免在程序的生命期中创建和删除大量对象。如果知道程序需要同一类型的大量对象,而且对象的生命周期都很短,就可以为这些对象创建一个池(pool)进行缓存。只要代码中需要一个对象,就可以向对象池请求.用完此对象时,要把它放回池中。对象池只创建一次对象,因此它们的构造函数只调用一次,而不是每次使用时都调用。因此,当构造函数要完成一些设置动作,而且这些设置可以应用与该对象的多次使用时,对象池就很适用。另外在非构造函数方法调用中要在对象上设置特定于实例的参数时,也很适用采用对象池。

      一个对象池的实现

     这里提供了一个池类对象的模板的实现,你可以在自己的程序中使用这个池实现。这个池在构造时会分配一大块(chunk)指定类的对象(这里的“块”,可以理解为包括许多对象,即一堆对象),并通过 acquireObject( ) 方法交出对象。当客户用完这个对象时,会通过 releaseObject( ) 方法将其返回。如果调用了 acquireObject( ) ,但是没有空闲的对象,池会分配另外一块对象。

      对象池实现中最难的一方面是要记录哪些对象是空闲的,哪些对象正在使用。这个实现采用以下做法,即把空闲的对象保存在一个队列中。每次客户请求一个对象时,池就会把队列中第一个对象交个客户。这个池不会显式地跟踪正在使用的对象。它相信客户在用完对象后会正确的把对象交还到池中。另外,这个池在一个向量中记录所有已分配的对象。这个向量仅在撤销池时才会用到,以便是否所有对象的内存,从而避免内存泄露。

     以下是类的定义,要注意,这个模板是基于相应的类类型(要在池中构造何种类型的对象)参数化的。

 

#include <queue>
#include <vector>
#include <stdexcept>
#include <memory>

using std::queue;
using std::vector;

//
// template class ObjectPool
//
// Provides an object pool that can be used with any class that provides a
// default constructor.
//
// The object pool constructor creates a pool of objects, which it hands out
// to clients when requested via the acquireObject() method. When a client is
// finished with the object it calls releaseObject() to put the object back
// into the object pool.
//
// The constructor and destructor on each object in the pool will be called only
// once each for the lifetime of the program, not once per acquisition and release.
//
// The primary use of an object pool is to avoid creating and deleting objects
// repeatedly. The object pool is most suited to applications that use large 
// numbers of objects for short periods of time.
//
// For efficiency, the object pool doesn't perform sanity checks.
// Expects the user to release every acquired object exactly once.
// Expects the user to avoid using any objects that he or she has released.
//
// Expects the user not to delete the object pool until every object
// that was acquired has been released. Deleting the object pool invalidates
// any objects that the user had acquired, even if they had not yet been released.
//
template <typename T>
class ObjectPool
{
 public:
  //
  // Creates an object pool with chunkSize objects.
  // Whenever the object pool runs out of objects, chunkSize
  // more objects will be added to the pool. The pool only grows:
  // objects are never removed from the pool (freed), until
  // the pool is destroyed.
  //
  // Throws invalid_argument if chunkSize is <= 0.
  //
  ObjectPool(int chunkSize = kDefaultChunkSize)
    throw(std::invalid_argument, std::bad_alloc);

  //
  // Frees all the allocated objects. Invalidates any objects that have
  // been acquired for use.
  //
  ~ObjectPool();

  //
  // Reserve an object for use. The reference to the object is invalidated
  // if the object pool itself is freed.
  // 
  // Clients must not free the object!
  //
  T& acquireObject();

  //
  // Return the object to the pool. Clients must not use the object after
  // it has been returned to the pool.
  //
  void releaseObject(T& obj);

 protected:
  //
  // mFreeList stores the objects that are not currently in use
  // by clients.
  //
  queue<T*> mFreeList;
  //
  // mAllObjects stores pointers to all the objects, in use
  // or not. This vector is needed in order to ensure that all
  // objects are freed properly in the destructor.
  //
  vector<T*> mAllObjects;

  int mChunkSize;
  static const int kDefaultChunkSize = 10;

  //
  // Allocates mChunkSize new objects and adds them
  // to the mFreeList.
  //
  void allocateChunk();
  static void arrayDeleteObject(T* obj);

 private:
  // Prevent assignment and pass-by-value
  ObjectPool(const ObjectPool<T>& src);
  ObjectPool<T>& operator=(const ObjectPool<T>& rhs);
};

template<typename T>
const int ObjectPool<T>::kDefaultChunkSize;

template <typename T>
ObjectPool<T>::ObjectPool(int chunkSize) throw(std::invalid_argument,
    std::bad_alloc) : mChunkSize(chunkSize)
{
    if (mChunkSize <= 0) {
        throw std::invalid_argument("chunk size must be positive");
    }
    // Create mChunkSize objects to start
    allocateChunk();
}

//
// Allocates an array of mChunkSize objects because that's
// more efficient than allocating each of them individually.
// Stores a pointer to the first element of the array in the mAllObjects
// vector. Adds a pointer to each new object to the mFreeList.
//
template <typename T>
void ObjectPool<T>::allocateChunk()
{
    T* newObjects = new T[mChunkSize];
    mAllObjects.push_back(newObjects);
    for (int i = 0; i < mChunkSize; i++) {
        mFreeList.push(&newObjects[i]);
    }
}

//
// Freeing function for use in the for_each algorithm in the
// destructor.
//
template<typename T>
void ObjectPool<T>::arrayDeleteObject(T* obj)
{
    delete [] obj;
}

template <typename T>
ObjectPool<T>::~ObjectPool()
{
    // free each of the allocation chunks
    for_each(mAllObjects.begin(), mAllObjects.end(), arrayDeleteObject);
}

template <typename T>
T& ObjectPool<T>::acquireObject()
{
    if (mFreeList.empty()) {
        allocateChunk();
    }
    T* obj = mFreeList.front();
    mFreeList.pop();
    return (*obj);
}

template <typename T>
void ObjectPool<T>::releaseObject(T& obj)
{
    mFreeList.push(&obj);
}

       对于这个类定义有几点需要强调。首先,要注意,对象是按引用获取和释放的,而不是按指针,这样可以避免客户通过指针管理或释放对象。接下来,注意对象池的用户通过模板参数来指定可以创建哪一个类的对象(即类名),通过构造函数指定分配的“块大小”。这个“块大小”控制着一次可创建的对象数。以下是定义 kDefaultChunkSize 的代码:

template<typename T>
const int ObjectPool<T>::kDefaultChunkSize;

     根据类定义,默认值 10 对于大多数使用来说可能都太小了.如果程序一次需要成千上万的对象,就应该使用一个更大、更适合的值。

     构造函数验证 chunkSize 参数,并调用 allocateChunk( ) 辅助方法来得到起始的对象分配。

template <typename T>
ObjectPool<T>::ObjectPool(int chunkSize) throw(std::invalid_argument,
    std::bad_alloc) : mChunkSize(chunkSize)
{
    if (mChunkSize <= 0) {
        throw std::invalid_argument("chunk size must be positive");
    }
    // Create mChunkSize objects to start
    allocateChunk();
}

     allocateChunk( ) 方法在连续地存储空间中分配 mChunkSize 个元素。它会在一个 mAllObjects vector 中存储对象数组的指针,并把各个对象压至 mFreeLlist queue。

//
// Allocates an array of mChunkSize objects because that's
// more efficient than allocating each of them individually.
// Stores a pointer to the first element of the array in the mAllObjects
// vector. Adds a pointer to each new object to the mFreeList.
//
template <typename T>
void ObjectPool<T>::allocateChunk()
{
    T* newObjects = new T[mChunkSize];
    mAllObjects.push_back(newObjects);
    for (int i = 0; i < mChunkSize; i++) {
        mFreeList.push(&newObjects[i]);
    }
}

     析构函数只是释放 allocateChunk( ) 中分配的所有对象数组。不过,它使用了 for_each( ) STL算法来做到这一点,在此向 for_each( ) 传递一个arrayDelete( ) 静态方法的指针,这个方法会对各个对象数组具体完成删除调用。

//
// Freeing function for use in the for_each algorithm in the
// destructor.
//
template<typename T>
void ObjectPool<T>::arrayDeleteObject(T* obj)
{
    delete [] obj;
}

template <typename T>
ObjectPool<T>::~ObjectPool()
{
    // free each of the allocation chunks
    for_each(mAllObjects.begin(), mAllObjects.end(), arrayDeleteObject);
}

     acquireObject( ) 会返回空闲列表中的队头对象,如果没有空闲对象则首先调用 allocateChunk( ) 。

template <typename T>
T& ObjectPool<T>::acquireObject()
{
    if (mFreeList.empty()) {
        allocateChunk();
    }
    T* obj = mFreeList.front();
    mFreeList.pop();
    return (*obj);
}

     最后,releaseObject( ) 将对象返回到空闲列表的队尾。

template <typename T>
void ObjectPool<T>::releaseObject(T& obj)
{
    mFreeList.push(&obj);
}

      使用对象池

     请考虑一个要从用户得到请求并处理这些请求的应用。这个应用很可能是图形前端和后端数据库之间的一个中间件。例如,这可能是一个航空预定系统或一个在线银行应用的一部分。你可能想把每个用户请求编码到一个对象中,这个类可能如下。

class UserRequest
{
public:
  UserRequest() {}
  ~UserRequest() {}

  // Methods to populate the request with specific information.
  // Methods to retrieve the request data.
  // (not shown)
         
protected:
  // data members (not shown)
};

     这里不用在程序的整个生命期中创建和删除大量请求,而是可以使用一个对象池。程序可能如下所示:

UserRequest& obtainUserRequest(ObjectPool<UserRequest>& pool)
{
  // Obtain a UserRequest object from the pool
  UserRequest& request = pool.acquireObject();

  // populate the request with user input
  // (not shown)

  return (request);
}

void processUserRequest(ObjectPool<UserRequest>& pool, UserRequest& req)
{
  // process the request
  // (not shown)

  // return the request to the pool
  pool.releaseObject(req);
}

int main(int argc, char** argv)
{
  ObjectPool<UserRequest> requestPool(1000);

  // Set up program
  // (not shown)

  while (true /* program is running */) {
    UserRequest& req = obtainUserRequest(requestPool);
    processUserRequest(requestPool, req);
  }

  return (0);
}

     另外,,,使用线程池也是一个提高C++程序效率的不错方式。线程池和对象池很相似,即不在程序的整个生命期中动态地创建和删除线程,而是创建一个线程池,按需使用池中的线程。如果程序要处理到来的网络请求,这种程序中常常会用到这种技术.web 服务器就可以维护一个线程池,以备查找页面,从而对到来的各个客户请求作出反应。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写高效的C++程序方法之使用对象池 的相关文章

  • 使用 gcc 在 Linux 上运行线程构建块 (Intel TBB)

    我正在尝试为线程构建块构建一些测试 不幸的是 我无法配置 tbb 库 链接器找不到库 tbb 我尝试在 bin 目录中运行脚本 但这没有帮助 我什至尝试将库文件移动到 usr local lib 但这又失败了 任何的意见都将会有帮助 确定您
  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • 结构化绑定中缺少类型信息

    我刚刚了解了 C 中的结构化绑定 但有一件事我不喜欢 auto x y some func is that auto正在隐藏类型x and y 我得抬头看看some func的声明来了解类型x and y 或者 我可以写 T1 x T2 y
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐

  • 遗传算法【matlab实现】(内含matlab基础式注释)

    最近在准备数学建模 凭借微弱的matlab基础学习遗传算法等一系列最优化算法实在是有点吃力 想着帮助自己消化吸收 帮助其他小白同学快速看懂代码 所以写了一篇blog 给出了遗传算法的大致实现思路 但重点是里面傻瓜式的代码注释 详细到了每一个
  • 《SOA 原理•方法•实践》作者毛新生先生签名售书活动

    SOA 原理 方法 实践 作者 毛新生 先生签名售书活动 2007 年8月24日 北京香格里拉 毛新生 先生将在 SOA 架构你的未来 大会现场签名售书 更多详情请访问 http www 900 ibm com cn express kin
  • 快手适合在美妆行业做广告投放吗?快手广告如何计费?

    根据统计数据 化妆品类电商评论中提及短视频声量较去年同期增长367 可见短视视频的广告投放方式深受广大用户喜欢 所以 快手适合做美妆广告吗 我们一起来看一看 一 快手平台适合做化妆品行业的广告吗 由于以下原因 快手很适合做美妆行业的广告投放
  • Deconfounded Visual Grounding

    本文内容仅代表个人理解 如有错误 欢迎指正 1 Background knowledge 这篇论文重度包含因果推理的相关知识 本文可能会粗略带过 感兴趣的可以去看看原论文 有比较详细的解释说明 Q1 什么是confounder confou
  • UIImagePickerController iOS11调起相册 中的照片被导航栏遮挡

    为了适配iOS11下来刷新下下偏移问题 全局设置了 UIScrollView appearance 的ContentInsetAdjustmentBehavior为UIScrollViewContentInsetAdjustmentNeve
  • golang编程cobra-cli库使用

    1 准备 1 1 资源和文档 库 https github com spf13 cobra 文档 https github com spf13 cobra blob master README md 1 2 下载 使用 go get命令下载
  • Centos7升级make和gcc版本到最新

    Background 遇到如下的问题可能就是你make和gcc的版本过低了 需要升级 These critical programs are missing or too old make compiler Check the INSTAL
  • OpenCV之FCN图像分割

    个人主页 风间琉璃 版权 本文由 风间琉璃 原创 在CSDN首发 需要转载请联系博主 如果文章对你有帮助 欢迎关注 点赞 收藏 一键三连 和订阅专栏哦 前言 Fully Convolutional Network FCN 是一种深度学习架构
  • 一文了解各大图数据库查询语言(Gremlin vs Cypher vs nGQL)

    文章的开头我们先来看下什么是图数据库 根据维基百科的定义 图数据库是使用图结构进行语义查询的数据库 它使用节点 边和属性来表示和存储数据 虽然和关系型数据库存储的结构不同 关系型数据库为表结构 图数据库为图结构 但不计各自的性能问题 关系型
  • hibernate 中常用的注解介绍

    定义表名称注解 Entity Table name SYS ROLES schema UAWP Cache usage CacheConcurrencyStrategy NONSTRICT READ WRITE public class R
  • 算法7-6:图的遍历——广度优先搜索(c语言)

    提交 统计 提问 题目描述 广度优先搜索遍历类似于树的按层次遍历的过程 其过程为 假设从图中的某顶点v出发 在访问了v之后依次访问v的各个未曾被访问过的邻接点 然后分别从这些邻接点出发依次访问它们的邻接点 并使 先被访问的顶点的邻接点 先于
  • 【NLP】LSTM总结记录

    目录 前言 RNN 梯度消失和梯度爆炸 梯度裁剪 relu leakyrelu等激活函数 Batch Normalization 批规范化 残差结构 LSTM 长短期记忆网络 LSTM形式 理解LSTM结构 梯度爆炸和消失的解决 pytor
  • MyBatis如何使用 transient 关键字

    MyBatis 是一个持久层框架 用于简化与数据库的交互 在 MyBatis 中 使用 transient 关键字可以标记 Java 对象中的字段 使其在序列化过程中被忽略 在本文中 我们将介绍 MyBatis 的基本用法和如何使用 tra
  • Shell脚本——函数用法

    目录 一 Shell函数的概念 1 Shell函数定义 方法一 方法二 二 函数的返回值 三 函数的传参 四 函数变量的作用范围 五 递归 1 阶乘 脚本命令 操作验证 2 递归目录文件 需求 脚本命令 操作验证 六 函数库 1 建立函数库
  • arxiv网站PDF论文下载速度提升

    arxiv属于国外网站 中国下载网速较慢 推荐使用中科院arxiv的镜像地址 http xxx itp ac cn PDF论文下载速度提升方法 把要访问 arxiv 链接中前面的域名从 https arxiv org 换成 http xxx
  • 花了两天,终于把 Python 的 setup.py 给整明白了

    1 为什么需要对项目分发打包 平常我们习惯了使用 pip 来安装一些第三方模块 这个安装过程之所以简单 是因为模块开发者为我们默默地为我们做了所有繁杂的工作 而这个过程就是 打包 打包 就是将你的源代码进一步封装 并且将所有的项目部署工作都
  • 【华为OD统一考试B卷

    2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为 2023A卷和2023B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是B卷 B卷对应之前专栏的20022部分考题以及新出的
  • 用MATLAB求序列反折

    在这个运算中 x n 以n 0为基准点 以纵轴为对称轴反折得到一个新的序列 即 y n x n 在MATLAB中提供了fliplr函数实现序列反折 fliplr用法是B fliplr A 其中A为矩阵或向量 如果A为矩阵 fliplr函数的
  • 论文笔记: Modeling Extreme Events in Time Series Prediction

    2019 KDD 0 摘要 时间序列预测是数据挖掘中一个深入研究的课题 尽管取得了相当大的改进 但最近基于深度学习的方法忽略了极端事件的存在 这导致将它们应用于实时序列时性能较弱 极端事件是罕见且随机的 但在许多实际应用中确实发挥了关键作用
  • 编写高效的C++程序方法之使用对象池

    对象池技术可以避免在程序的生命期中创建和删除大量对象 如果知道程序需要同一类型的大量对象 而且对象的生命周期都很短 就可以为这些对象创建一个池 pool 进行缓存 只要代码中需要一个对象 就可以向对象池请求 用完此对象时 要把它放回池中 对