C++对象池设计与实现

目录

一、对象池简介

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.mfbz.cn/a/695906.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】插入排序(经典算法,建议收藏!!!)

目录 1、原理2、代码展示3、解析代码4、适用场景 1、原理 插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;其原理可以简述如下&#xff1a; 1.分已排序区间和未排序区间: 将数组分为已排序区间和未排序区间。初始时&#xff0c;已排序区…

51单片机-独立按键控制灯灯灯

目录 简介: 一. 1个独立按钮控制一个灯例子 二. 在加一个独立按键,控制第二个灯 三. 第一个开关 开灯, 第二个开关关灯 四. 点一下开灯,在点一下关灯 五. 总结 简介: 51 单片机具有强大的控制能力&#xff0c;而独立按键则提供了一种简单的输入方式。 当把独立按键与 …

Vue 2看这篇就够了

Vue 2 技术文档 Vue.js 是一款用于构建用户界面的渐进式框架。与其他重量级框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整合。而 Vue.js 2&#xff08;以下简称 Vue…

Leetcode 力扣113. 路径总和 II (抖音号:708231408)

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&a…

【设计模式】结构型-桥接模式

当抽象与实现&#xff0c;各自独立&#xff0c; 桥接模式&#xff0c;如彩虹桥&#xff0c;连接两岸。 文章目录 一、类爆炸与代码重复二、桥接模式三、桥接模式的核心组成四、运用桥接模式五、桥接模式的应用场景六、小结推荐阅读 一、类爆炸与代码重复 场景假设&#xff1a…

标量、向量、矩阵、张量是什么?

参考视频&#xff1a;标量、向量、矩阵、张量-机器学习-李文哲AI人工智能培训_哔哩哔哩_bilibili 参考资料&#xff1a;深度学习基础&#xff1a;标量、向量、矩阵、张量_深度学习本质是矩阵-CSDN博客 标量是一个独立存在的数&#xff0c;比如线性代数中的一个实数5就可以被看…

每日一题——Python实现PAT乙级1019 数字黑洞(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 点评代码的优缺点&#xff1a; 时间复杂度&#xff1a; 空间复杂度&#…

DAMA学习笔记(一)-数据管理

1.引言 数据管理(Data Management) 是为了 交付、 控制、 保护 并 提升 数据和信息资产的 价值 , 在其整个生命周期中制订 计划、 制度、 规程和实践 活动, 并 执行 和 监督 的过程。 数据管理专业人员(Data Management Professional) 是指 从事数据管理各方面的工作…

Cinema 4D 2024 软件安装教程、附安装包下载

Cinema 4D 2024 Cinema 4D&#xff08;C4D&#xff09;是一款由Maxon开发的三维建模、动画和渲染软件&#xff0c;广泛用于电影制作、广告、游戏开发、视觉效果等领域。Cinema 4D允许用户创建复杂的三维模型&#xff0c;包括角色、场景、物体等。它提供了多种建模工具&#x…

Flink 基于 TDMQ Apache Pulsar 的离线场景使用实践

背景 Apache Flink 是一个开源的流处理和批处理框架&#xff0c;具有高吞吐量、低延迟的流式引擎&#xff0c;支持事件时间处理和状态管理&#xff0c;以及确保在机器故障时的容错性和一次性语义。Flink 的核心是一个分布式流数据处理引擎&#xff0c;支持 Java、Scala、Pytho…

数据库期末设计——图书管理系统

目录 1.前置软件以及开发环境&#xff1a; 2.开发过程讲解 代码环节&#xff1a; 数据库代码 1.BookDao.java 2.BookTypeDao.java 3.UserDao.java 4.Book.java 5.BookType.java 6.User.java 7.DbUtil.java 8.Stringutil.java 9.BookAddInterFrm.java 10.BookMan…

redis 05 复制 ,哨兵

01.redis的复制功能&#xff0c;使用命令slaveof 2. 2.1 2.2 3. 3.1 3.1.1 3.1.2 3.1.3 4 4.1 4.2 例子 5.1 这里是从客户端发出的指令 5.2 套接字就是socket 这里是和redis事件相关的知识 5.3 ping一下

Capture One 23 软件安装教程、附安装包下载

Capture One Capture One 23 是一款功能极为全面的图片处理软件&#xff0c;为用户提供了真正的逼真色彩处理和无缝衔接的编辑体验&#xff0c;以及业界最快的联机拍摄功能&#xff0c;可以满足用户在图像创作上的所有功能&#xff0c;如创作全景拼接大图、高级色彩调整、遮罩…

【Golang】Map 稳定有序遍历的实现与探索:保序遍历之道

【Golang】Map 稳定有序遍历的实现与探索&#xff1a;保序遍历之道 大家好 我是寸铁&#x1f44a; 总结了一篇【Golang】Map 稳定有序遍历的实现与探索&#xff1a;保序遍历之道✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言&#x1f34e; 在计算机科学中&#xff0c;数据结…

GAT1399协议分析(7)--pycharm anaconde3 配置pyside2

一、安装pyside2 pip install pyside2 二、配置qtdesigner File->Settings->Tools->External tools 点击添加&#xff0c;添加QtDesigner&#xff0c; 注意designer.exe路径&#xff0c;需要在虚拟环境下。 三、添加pySide2 File->Settings->Tools->Ext…

Long-Context LLM综述

目录 前言1. Preliminary2. 方法分类3. 长度外推3.1 位置外推和插值位置外推插值 3.2 上下文窗口分割与滑动3.3 提示压缩 4. 注意力近似4.1 低秩分解4.2 稀疏注意力4.3 Softmax-free Attention 5. Attention-free Transformers5.1 状态空间模型(State Space Model, SSM)5.2 位置…

Spark作业运行异常慢的问题定位和分析思路

一直很慢 &#x1f422; 运行中状态、卡住了&#xff0c;可以从以下两种方式入手&#xff1a; 如果 Spark UI 上&#xff0c;有正在运行的 Job/Stage/Task&#xff0c;看 Executor 相关信息就好。&#x1f4bb; 第一步&#xff0c;如果发现卡住了&#xff0c;直接找到对应的…

【每日算法】

算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归) 文章目录 算法第15天| (二叉树part02)层序遍历、226.翻转二叉树(优先掌握递归)、101. 对称二叉树(优先掌握递归)一、层序遍历二、226. 翻转二叉树(优先掌握递归)三、101. 对…

【机器学习】Python与深度学习的完美结合——深度学习在医学影像诊断中的惊人表现

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、深度学习在医学影像诊断中的突破1. 技术原理2. 实际应用3. 性能表现 三、深度学习在医学影像诊断中的惊人表现1. 提高疾病诊断准确率2. 辅助制定治疗方案 四、深度学习对医疗行业的影响和推动作用 一、引言 随着…

MYSQL基础_02_MySQL环境搭建

第02章_MySQL环境搭建 1. MySQL的卸载 步骤1&#xff1a;停止MySQL服务 在卸载之前&#xff0c;先停止MySQL8.0的服务。按键盘上的“Ctrl Alt Delete”组合键&#xff0c;打开“任务管理器”对话框&#xff0c;可以在“服务”列表找到“MySQL8.0”的服务&#xff0c;如果现…