C++对象池设计与实现

news2024/10/5 13:35:55

目录

一、对象池简介

1.1 池化技术

1.2 什么是对象池

1.3 对象池分配策略

二、C++ new和delete运算符重载

三、实现一个对象池框架

3.1 策略接口

四、实现几种对象池的分配策略

4.1 数组策略

4.2 堆策略

​编辑

4.3 栈策略

4.4 区块策略


一、对象池简介

1.1 池化技术

线程池、连接池、内存池

池化技术共同点

提前创建资源,以备不时之需时重复利用,极致的提升性能。

由于在实际应用里分配内存、创建进程、线程,都会涉及到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池等技术来提升程序的性能。

1.2 什么是对象池

对象池简介

对象池的实现和内存池的实现原理很像:都是-开始申请大内存空间, 然后把大内存分配成小内存空间,当需要使用的时候直接分配使用,不再向系统申请内存空间,也不直接释放内存空间。使用完之后都是放回池子里。

注意

对象池其实就是一种特殊的内存池,仅分配固定大小的内存。

1.3 对象池分配策略

● 基于数组的策略

● 基于堆的策略

● 基于栈的策略

● 基于区块的策略

课程里我们会实现以上几种对象池的分配策略,从最简单的数组策略到比较复杂的区块策略。每种策略都有各自的特点和适用场景。

二、C++ new和delete运算符重载

class A
{
public:
    void * operator new(size_t n)
    {
        std::cout << "A new" << std::endl; 
        return ::malloc(n);
    }
    void operator delete(void * p)
    {
        std::cout << "A delete" << std::endl;
        ::free(p);
    }
};

三、实现一个对象池框架

3.1 策略接口

template <typename T>
class Allocator {
public:
    virtual T * allocate() = 0;
    virtual void deallocate(T * p) = 0;
};

策略接口函数:

allocate:分配内存

deallocate:回收内存

现在创建和析构对象不需要用new和free了,而是用对象池的创建和析构函数。

// a.h

#pragma once
#include <iostream>
#include "object_pool.h"
#include "malloc_allocator.h"

using namespace huan::object;

class A
{
private:
    typedef ObjectPool<A, MallocAllocator<A>> ObjectPool;
    static ObjectPool pool;
public:
    A()
    {
        std::cout << "A construct" << std::endl;
    }
    ~A()
    {
        std::cout << "A destruct" << std::endl;
    }

    void * operator new(size_t n)
    {
        std:: cout << "A new" << std::endl;
        return pool.allocate(n);
    }

    void operator delete(void * p)
    {
        std::cout << "A delete" << std::endl;
        pool.deallocate(p);
    }
};

A::ObjectPool A::pool;

// object_pool.h

#pragma once

#include <stdexcept>

namespace huan
{
    namespace object
    {
        template <typename T, typename Allocator>
        class ObjectPool
        {
        public:
            ObjectPool() = default;
            ~ObjectPool() = default;

            void * allocate(size_t n)
            {
                if (sizeof(T) != n)
                    throw std::bad_alloc();
                return m_allocator.allocate();
            }

            void deallocate(void * p)
            {
                m_allocator.deallocate(static_cast<T *>(p));
            }
        private:
            Allocator m_allocator;
        };
        
    }
}

// malloc_allocator.h

#pragma once

#include "allocator.h"

namespace huan
{
    namespace object
    {
        template <typename T>
        class MallocAllocator : public Allocator<T>
        {
        public:
            MallocAllocator() = default;
            ~MallocAllocator() = default;

            virtual T * allocate()
            {
                auto p = ::malloc(sizeof(T));
                return reinterpret_cast<T *>(p);
            }

            virtual void deallocate(T * p)
            {
                ::free(p);
            }
        };
    }
}

// allocator.h

#pragma once

namespace huan
{
    namespace object
    {
        template <typename T>
        class Allocator
        {
        public:
            virtual T * allocate() = 0;
            virtual void deallocate(T * p) = 0;
        };
    }
}

四、实现几种对象池的分配策略

4.1 数组策略

#include <src/a.h>

int main()
{
    A * arr[max_size] = { nullptr };

    for (int i = 0; i < max_size; i++)
    {
        A * a = new A();
        arr[i] = a;
    }

    for (int i = 0; i < max_size; i++)
    {
        delete arr[i];
    }

    return 0;
}

#pragma once

#include "allocator.h"

namespace huan
{
    namespace object
    {
        template <typename T, int N>
        class ArrayAllocator : public Allocator<T>
        {
        public:
            ArrayAllocator()
            {
                for (int i = 0; i < N; i++)
                {
                    m_used[i] = false;
                }
            }
            ~ArrayAllocator() = default;

            virtual T * allocate()
            {
                for (int i = 0; i < N; i++)
                {
                    if (!m_used[i])
                    {
                        m_used[i] = true;
                        return reinterpret_cast<T *>(&m_data[sizeof(T) * i]);
                    }
                }

                // 如果没找到
                throw std::bad_alloc();
            }

            virtual void deallocate(T * p)
            {
                auto i = ((unsigned char *)p - m_data) / sizeof(T);
                m_used[i] = false;
            }

        private:
            unsigned char m_data[sizeof(T) * N];
            bool m_used[N];
        };
    }
}

注意:基于数组的时间复杂度高 O(n)

4.2 堆策略

使用大根堆,heap第一个位置总是空闲的。在插入一个对象后,堆会重新把一个没有使用的位置放到第一位

// heap_allocator.h

#include <algorithm>
#include "allocator.h"

namespace huan
{
    namespace object
    {
        template <typename T, int N>
        class HeapAllocator : public Allocator<T>
        {
        public:
            enum State
            {
                FREE = 1,
                USED = 0,
            };

            struct Entry
            {
                State state;    // 状态
                T * p;          // 对象指针

                bool operator < (const Entry & other) const
                {
                    return state < other.state;
                }
            };

            HeapAllocator()
            {
                m_available = N;

                for (int i = 0; i < N; i++)
                {
                    m_entry[i].state = FREE;    // 未使用
                    m_entry[i].p = reinterpret_cast<T *>(&m_data[sizeof(T) * i]);
                }

                // 调用生成大堆的算法
                std::make_heap(m_entry, m_entry + N);
            }
            ~HeapAllocator() = default;

            virtual T * allocate()
            {
                if (m_available <= 0)
                    throw std::bad_alloc();
                Entry e = m_entry[0];
                std::pop_heap(m_entry, m_entry + N);
                m_available--;

                m_entry[m_available].state = USED;
                m_entry[m_available].p = nullptr;
                    
                return e.p;
            }

            virtual void deallocate(T * p)
            {
                if (p == nullptr || m_available >= N)
                    return;
                m_entry[m_available].state = FREE;
                m_entry[m_available].p = reinterpret_cast<T *>(p);

                m_available++;

                std::push_heap(m_entry, m_entry + N);
            }

        private:
            unsigned char m_data[sizeof(T) * N];
            Entry m_entry[N];
            int m_available;
        };
    }
}

时间复杂度 O(log n)

4.3 栈策略

4.4 区块策略

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

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

相关文章

SAS:coalescec函数和cmiss函数的应用及拓展

背景&#xff1a;CRF中收集了每个受试者3个RACE方面的信息&#xff0c;SDTM SPEC规定了RACE的生成规则为&#xff1a;若收集了多个RACE&#xff0c;RACE“MULTIPLE”&#xff0c;详细的RACE信息记录在SUPPDM中&#xff1b;若仅收集到一个RACE&#xff0c;则RACE等于RACE1-RACE3…

ROS 获取激光雷达数据(C++实现)

ROS 获取激光雷达数据&#xff08;C实现&#xff09; 实现思路 在机器人ROS系统中&#xff0c;激光雷达通常会有一个对应的节点&#xff0c;这个节点一般是由雷达的厂商提供&#xff0c;我们只需要简单的配置以下端口参数&#xff0c;就能和激光雷达的电路系统建立连接&#…

贪吃蛇双人模式设计(2)

敲上瘾-CSDN博客控制台程序设置_c语言控制程序窗口大小-CSDN博客贪吃蛇小游戏_贪吃蛇小游戏csdn-CSDN博客​​​​​​​ 一、功能实现&#xff1a; 玩家1使用↓ → ← ↑按键来操作蛇的方向&#xff0c;使用右Shift键加速&#xff0c;右Ctrl键减速玩家2使用W A S D按键来操…

向AI请教如何说不

面对父母的催婚&#xff0c;你可以采取以下几个步骤来进行沟通和表达自己的立场&#xff1a; 理解与尊重&#xff1a;首先&#xff0c;要理解父母催婚背后的关心和期望。他们可能出于对你未来幸福和生活稳定的考虑。表达对他们关心的感激&#xff0c;这有助于建立良好的沟通基础…

入门级的卷积神经网络训练识别手写数字-小白轻松上手-含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89374845 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

软件试运行方案(Word)

软件试运行方案&#xff08;直接套用实际项目&#xff0c;原件获取通过本文末个人名片直接获取。&#xff09; 一、试运行目的 二、试运行的准备 三、试运行时间 四、试运行制度 五、试运行具体内容与要求

Java 跳转语句 return,break和continue区别

一、总结 1、return 是结束方法 2、break 是跳出循环 3、continue 是终止本次循环继续下次循环 二、示例 public static void printWithReturn() {for (int x 1; x < 9; x) {for (int y 1; y < x; y) {System.out.print(y "*" x "" (x * …

ChatGPT Prompt技术全攻略-总结篇:Prompt工程技术的未来发展

系列篇章&#x1f4a5; No.文章1ChatGPT Prompt技术全攻略-入门篇&#xff1a;AI提示工程基础2ChatGPT Prompt技术全攻略-进阶篇&#xff1a;深入Prompt工程技术3ChatGPT Prompt技术全攻略-高级篇&#xff1a;掌握高级Prompt工程技术4ChatGPT Prompt技术全攻略-应用篇&#xf…

有趣的数学 为什么绝对值和模都用两个竖线表示?

绝对值和模都可以使用两个竖线表示&#xff0c;是因为它们在数学概念上有相似的性质&#xff0c;不过是应用场景不同。 绝对值&#xff08;Absolute Value&#xff09;&#xff1a; 绝对值是一个实数的非负值。它表示一个数在数轴上距离原点的距离。例如&#xff0c; 和 。 模&…

滑动窗口算法:巧妙玩转数据的窗外世界

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 滑动窗口是什么&#xff1f; 二 相关题目解析 1. 长度最小的子数组 &#x1f973;题目解析 &#x1f973;算法原理 ✏️思路1 暴力枚举出所有子数组之和 ✏️思路2 滑动窗…

虚幻引擎5 Gameplay框架(五)

Gameplay重要类及重要功能使用方法&#xff08;四&#xff09; DeveloperSetting DeveloperSetting是在虚幻引擎中是一个基类&#xff0c;主要用于创建和管理开发者设置相关的类。这类设置允许开发者自定义或调整项目中的各种配置选项&#xff0c;而无需直接修改代码或构建设置…

【内存管理】内存管理概述

文章目录 内存管理硬件结构早期内存的使用方法分段分页逻辑地址&#xff0c;线性地址&#xff08;intel架构&#xff09;虚拟地址物理地址结构图 虚拟地址到物理地址的转换内存管理总览系统调用vm_area_struct缺页中断伙伴系统slab分配器页面回收反向映射KSMhuge page页迁移内存…

【内存管理】内存布局

ARM32位系统的内存布局图 32位操作系统的内存布局很经典&#xff0c;很多书籍都是以32位系统为例子去讲解的。32位的系统可访问的地址空间为4GB&#xff0c;用户空间为1GB ~ 3GB&#xff0c;内核空间为3GB ~ 4GB。 为什么要划分为用户空间和内核空间呢&#xff1f; 一般处理器…

FlashBrowser

本例&#xff1a;windows10 下载FlashBrowser 解决flash失效问题&#xff0c;更换浏览器 https://www.flash.cn/ 下载FlashBrowser浏览器

【Windows】UWP - Application Frame 窗口句柄溯源

目录 一、问题描述 二、解决方案 三、测试代码 参考文献 本文出处链接&#xff1a;[https://blog.csdn.net/qq_59075481/article/details/139574981]。 一、问题描述 当 GUI 线程的窗口属于 Windows/UWP 应用程序时&#xff0c;它们始终由进程 ApplicationFrameHost 托管…

使用DPO微调大模型Qwen2详解

简介 基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback&#xff0c;RLHF) 事实上已成为 GPT-4 或 Claude 等 LLM 训练的最后一步&#xff0c;它可以确保语言模型的输出符合人类在闲聊或安全性等方面的期望。但传统的RLHF比较复杂&#xff0c;且还需要奖励…

【教学类-64-02】20240610色块眼力挑战(二)-2-25宫格色差10-100(10倍)(星火讯飞)

背景需求 以下的色块眼里挑战需要人工筛选图片&#xff0c;非常繁琐。 【教学类-64-01】20240607色块眼力挑战&#xff08;一&#xff09;-0-255随机底色-CSDN博客文章浏览阅读446次&#xff0c;点赞12次&#xff0c;收藏5次。【教学类-64-01】20240607色块眼力挑战&#xff…

web入门(1)---6.10

总结&#xff1a; 多做一点NSSCTF的新手赛&#xff0c;了解基本题型&#xff0c;然后打牢基础知识 谢队讲解 攻防世界 Web入门题 讲解_哔哩哔哩_bilibili 题目来源&#xff1a;攻防世界新手区 1.view_source 查看源代码 2.get_post 收获&#xff1a; get方法是直接在url…

攻防世界---misc---BotW-

1、下载附件是一张图片 2、查看图片属性&#xff0c;用winhex分析&#xff0c;没有发现奇怪的地方&#xff0c;用binwalk&#xff0c;接着使用foremost 3、得到两张图片&#xff0c;一张是原图&#xff0c;一张是特殊的字符 4、经过查阅资料得知&#xff0c;这是希卡文字&#…

数据中心基础设施智能运维

数据中心基础设施智能运维 随着科技的飞速发展&#xff0c;数据中心作为信息社会的核心基础设施&#xff0c;扮演着越来越重要的角色。然而&#xff0c;传统的运维模式由于对人力资源的高度依赖&#xff0c;已无法满足现代数据中心对高效、安全和可持续运维的要求。华为的《数…