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 区块策略

相关推荐

  1. C++面向对象程序设计之类对象(1)

    2024-06-09 10:38:04       9 阅读
  2. C++类对象------实现------堆

    2024-06-09 10:38:04       13 阅读
  3. 线程设计---C++

    2024-06-09 10:38:04       24 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 10:38:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 10:38:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 10:38:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 10:38:04       18 阅读

热门阅读

  1. Npm发布自己的插件包

    2024-06-09 10:38:04       7 阅读
  2. Linux基本指令查询硬件信息001

    2024-06-09 10:38:04       9 阅读
  3. 360数字安全:2024年3月勒索软件流行态势分析报告

    2024-06-09 10:38:04       8 阅读
  4. Redis中的发布/订阅模式:构建灵活的消息系统

    2024-06-09 10:38:04       8 阅读
  5. 2024年OceanBase数据库中级培训课程

    2024-06-09 10:38:04       9 阅读
  6. cocos入门9:三维向量点乘

    2024-06-09 10:38:04       11 阅读
  7. TypeScript 中的 tsconfig.json

    2024-06-09 10:38:04       7 阅读
  8. Tomcat 启动闪退问题解决方法

    2024-06-09 10:38:04       7 阅读
  9. WDF驱动开发-PNP和电源管理(二)

    2024-06-09 10:38:04       14 阅读
  10. Linux下tcpwrappers防火墙介绍

    2024-06-09 10:38:04       7 阅读
  11. Mysql的脏读、幻读、不可重复读

    2024-06-09 10:38:04       7 阅读
  12. PCL 指数函数回归(二维)

    2024-06-09 10:38:04       6 阅读
  13. Stable Diffusion之最全详解图解

    2024-06-09 10:38:04       6 阅读
  14. 频率域,空间域以及频率域和空间域如何获取

    2024-06-09 10:38:04       10 阅读