首页 > 系统相关 >学懂C++(四十四):C++ 自定义内存管理的深入解析:内存池与自定义分配器

学懂C++(四十四):C++ 自定义内存管理的深入解析:内存池与自定义分配器

时间:2024-08-26 13:57:49浏览次数:14  
标签:std 自定义 void C++ CustomAllocator 内存 分配器

目录

1. 内存池(Memory Pool)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

2. 自定义分配器(Custom Allocators)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

高级自定义分配器示例

代码解析

总结


        C++作为一种高性能编程语言,在处理内存管理方面提供了极大的灵活性。默认情况下,C++使用操作系统提供的内存分配和释放机制(如newdeletemallocfree)。然而,在某些高性能或特定需求的应用中,默认的内存管理机制可能无法满足需求。这时,自定义内存管理技术,如内存池(Memory Pool)和自定义分配器(Custom Allocators),可以提供更高效、更灵活的内存管理方案。

        本文将详细介绍内存池和自定义分配器在C++中的应用,包含其概念、模型、特点、核心点、实现方法、适用场景,以及经典示例代码和详细解析。

1. 内存池(Memory Pool)

概念

        内存池是一种自定义内存管理机制,通过预分配一大块内存并将其划分成多个固定大小的块,从而实现快速的内存分配和释放。这种技术特别适用于频繁分配和释放相同大小对象的场景。

模型

  • 预分配内存块:一次性分配一大块连续的内存。
  • 分割内存块:将大块内存划分为多个固定大小的小块。
  • 空闲块管理:维护一个空闲块的列表,用于快速分配和释放内存。

特点

  • 高效性:内存分配和释放操作可以在常数时间内完成。
  • 减少碎片:通过固定大小的块减少内存碎片问题。
  • 预分配:内存池一次性分配大块内存,减少多次分配带来的开销。

核心点

  • 预先分配足够大的内存块。
  • 维护空闲块列表,实现快速分配和释放。

实现

#include <iostream>
#include <vector>
#include <cassert>

class MemoryPool
{
private:
    struct Block
    {
        Block* next;
    };

    Block* freeBlocks; // 空闲块链表头指针
    std::vector<void*> pool; // 用于存储内存块的指针数组

    void allocatePool(size_t blockSize, size_t blockCount)
    {
        // 分配一大块连续内存
        void* newPool = operator new(blockSize * blockCount);
        pool.push_back(newPool);

        Block* head = static_cast<Block*>(newPool);
        freeBlocks = head;

        // 初始化空闲块链表
        for (size_t i = 1; i < blockCount; ++i)
        {
            head->next = reinterpret_cast<Block*>(reinterpret_cast<char*>(head) + blockSize);
            head = head->next;
        }
        head->next = nullptr;
    }

public:
    MemoryPool(size_t blockSize, size_t blockCount)
    {
        allocatePool(blockSize, blockCount);
    }

    ~MemoryPool()
    {
        // 释放所有内存块
        for (void* ptr : pool)
        {
            operator delete(ptr);
        }
    }

    void* allocate()
    {
        if (!freeBlocks)
        {
            // 如果没有空闲块,则分配新的内存池
            throw std::bad_alloc();
        }

        // 从空闲块链表中取出一个块
        Block* block = freeBlocks;
        freeBlocks = freeBlocks->next;
        return block;
    }

    void deallocate(void* ptr)
    {
        // 将块返回到空闲块链表中
        Block* block = static_cast<Block*>(ptr);
        block->next = freeBlocks;
        freeBlocks = block;
    }
};

适用场景

  • 高性能应用:如游戏开发中的对象池、网络服务器的连接管理等。
  • 频繁分配和释放相同大小对象:如内存块、节点对象等。

经典示例实现

#include <iostream>
#include <vector>
#include <cassert>

class MemoryPool
{
private:
    struct Block
    {
        Block* next;
    };

    Block* freeBlocks; // 空闲块链表头指针
    std::vector<void*> pool; // 用于存储内存块的指针数组

    void allocatePool(size_t blockSize, size_t blockCount)
    {
        // 分配一大块连续内存
        void* newPool = operator new(blockSize * blockCount);
        pool.push_back(newPool);

        Block* head = static_cast<Block*>(newPool);
        freeBlocks = head;

        // 初始化空闲块链表
        for (size_t i = 1; i < blockCount; ++i)
        {
            head->next = reinterpret_cast<Block*>(reinterpret_cast<char*>(head) + blockSize);
            head = head->next;
        }
        head->next = nullptr;
    }

public:
    MemoryPool(size_t blockSize, size_t blockCount)
    {
        allocatePool(blockSize, blockCount);
    }

    ~MemoryPool()
    {
        // 释放所有内存块
        for (void* ptr : pool)
        {
            operator delete(ptr);
        }
    }

    void* allocate()
    {
        if (!freeBlocks)
        {
            // 如果没有空闲块,则分配新的内存池
            throw std::bad_alloc();
        }

        // 从空闲块链表中取出一个块
        Block* block = freeBlocks;
        freeBlocks = freeBlocks->next;
        return block;
    }

    void deallocate(void* ptr)
    {
        // 将块返回到空闲块链表中
        Block* block = static_cast<Block*>(ptr);
        block->next = freeBlocks;
        freeBlocks = block;
    }
};

class Widget
{
public:
    int x, y, z;

    Widget(int a, int b, int c) : x(a), y(b), z(c) {}

    void display()
    {
        std::cout << "Widget(" << x << ", " << y << ", " << z << ")" << std::endl;
    }

    // 重载new操作符
    static void* operator new(size_t size)
    {
        return pool.allocate();
    }

    // 重载delete操作符
    static void operator delete(void* ptr)
    {
        pool.deallocate(ptr);
    }

private:
    static MemoryPool pool;
};

// 初始化静态内存池
MemoryPool Widget::pool(sizeof(Widget), 10);

int main()
{
    std::vector<Widget*> widgets;

    // 分配Widget对象
    for (int i = 0; i < 10; ++i)
    {
        widgets.push_back(new Widget(i, i + 1, i + 2));
    }

    // 显示Widget对象
    for (Widget* widget : widgets)
    {
        widget->display();
    }

    // 释放Widget对象
    for (Widget* widget : widgets)
    {
        delete widget;
    }

    return 0;
}

代码解析

  1. MemoryPool:实现内存池管理,包括分配和释放内存块。
  2. Widget:示例对象类,重载了newdelete操作符以使用内存池进行内存管理。
  3. main函数:展示了如何使用内存池分配和释放Widget对象。

2. 自定义分配器(Custom Allocators)

概念

自定义分配器是C++标准库(STL)中的一种机制,允许开发者控制容器的内存分配策略。通过自定义分配器,开发者可以替换默认的内存分配方式,以满足特定的性能需求或内存管理策略。

模型

  • 分配器类:实现内存分配和释放的接口(如allocatedeallocate方法)。
  • 容器类:使用自定义分配器进行内存管理。

特点

  • 灵活性:开发者可以完全控制内存分配和释放。
  • 可扩展性:可以根据具体需求实现不同的内存管理策略。

核心点

  • 实现分配器类,提供allocatedeallocate方法。
  • 在容器中使用自定义分配器。

实现

#include <iostream>
#include <memory>
#include <vector>

template <typename T>
class CustomAllocator
{
public:
    using value_type = T;

    CustomAllocator() = default;

    template <typename U>
    CustomAllocator(const CustomAllocator<U>&) {}

    T* allocate(std::size_t n)
    {
        std::cout << "Allocating " << n << " element(s)" << std::endl;
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* ptr, std::size_t n)
    {
        std::cout << "Deallocating " << n << " element(s)" << std::endl;
        ::operator delete(ptr);
    }
};

template <typename T, typename U>
bool operator==(const CustomAllocator<T>&, const CustomAllocator<U>&)
{
    return true;
}

template <typename T, typename U>
bool operator!=(const CustomAllocator<T>&, const CustomAllocator<U>&)
{
    return false;
}

适用场景

  • 高性能应用:如游戏开发中的对象池、实时系统的内存管理等。
  • 特定内存管理需求:如内存对齐、内存池管理等。

经典示例实现

#include <iostream>
#include <memory>
#include <vector>

// 自定义分配器类
template <typename T>
class CustomAllocator
{
public:
    using value_type = T;

    CustomAllocator() = default;

    template <typename U>
    CustomAllocator(const CustomAllocator<U>&) {}

    T* allocate(std::size_t n)
    {
        std::cout << "Allocating " << n << " element(s)" << std::endl;
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* ptr, std::size_t n)
    {
        std::cout << "Deallocating " << n << " element(s)" << std::endl;
        ::operator delete(ptr);
    }
};

template <typename T, typename U>
bool operator==(const CustomAllocator<T>&, const CustomAllocator<U>&)
{
    return true;
}

template <typename T, typename U>
bool operator!=(const CustomAllocator<T>&, const CustomAllocator<U>&)
{
    return false;
}

int main()
{
    // 使用自定义分配器的vector容器
    std::vector<int, CustomAllocator<int>> vec;

    // 添加元素
    for (int i = 0; i < 5; ++i)
    {
        vec.push_back(i);
    }

    // 显示元素
    for (int i : vec)
    {
        std::cout << i << std::endl;
    }

    return 0;
}

代码解析

  1. CustomAllocator:实现自定义分配器,包括allocatedeallocate方法。
  2. operator==operator!=:定义分配器的比较操作符。
  3. main函数:展示了如何使用自定义分配器创建vector容器,并进行元素的添加和显示。

高级自定义分配器示例

#include <iostream>
#include <memory>
#include <vector>

// 高级自定义分配器类
template <typename T>
class AdvancedAllocator
{
public:
    using value_type = T;

    AdvancedAllocator() = default;

    template <typename U>
    AdvancedAllocator(const AdvancedAllocator<U>&) {}

    T* allocate(std::size_t n)
    {
        std::cout << "Advanced Allocator: Allocating " << n << " element(s)" << std::endl;
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* ptr, std::size_t n)
    {
        std::cout << "Advanced Allocator: Deallocating " << n << " element(s)" << std::endl;
        ::operator delete(ptr);
    }

    template <typename U, typename... Args>
    void construct(U* p, Args&&... args)
    {
        std::cout << "Advanced Allocator: Constructing element" << std::endl;
        ::new((void*)p) U(std::forward<Args>(args)...);
    }

    template <typename U>
    void destroy(U* p)
    {
        std::cout << "Advanced Allocator: Destroying element" << std::endl;
        p->~U();
    }
};

template <typename T, typename U>
bool operator==(const AdvancedAllocator<T>&, const AdvancedAllocator<U>&)
{
    return true;
}

template <typename T, typename U>
bool operator!=(const AdvancedAllocator<T>&, const AdvancedAllocator<U>&)
{
    return false;
}

int main()
{
    // 使用高级自定义分配器的vector容器
    std::vector<int, AdvancedAllocator<int>> vec;

    // 添加元素
    for (int i = 0; i < 5; ++i)
    {
        vec.push_back(i);
    }

    // 显示元素
    for (int i : vec)
    {
        std::cout << i << std::endl;
    }

    return 0;
}

代码解析

  1. AdvancedAllocator:在CustomAllocator基础上,增加了constructdestroy方法,用于元素的构造和析构。
  2. operator==operator!=:定义分配器的比较操作符。
  3. main函数:展示了如何使用高级自定义分配器创建vector容器,并进行元素的添加和显示。

总结

        自定义内存管理技术,如内存池和自定义分配器,在C++中提供了强大的内存管理能力。它们不仅能够提高内存管理的效率,还能满足特定应用场景下的内存管理需求。

  • 内存池(Memory Pool):通过预分配一大块内存并将其划分成多个固定大小的块,实现高效的内存分配和释放。适用于高性能应用和频繁分配和释放相同大小对象的场景。
  • 自定义分配器(Custom Allocators):允许开发者控制容器的内存分配策略,满足特定的性能需求或内存管理策略。适用于高性能应用和特定内存管理需求的场景。

        通过以上示例代码和详细解析,开发者可以更好地理解和应用自定义内存管理技术,从而提高软件质量和开发效率。

标签:std,自定义,void,C++,CustomAllocator,内存,分配器
From: https://blog.csdn.net/martian665/article/details/141561146

相关文章

  • c++关键字
    关键字作用:关键字是C++中预先保留的单词(标识符)在定义变量或者常量时候,不要用关键字C++关键字如下:关键字1.asmasm(指令字符串):允许在C++程序中嵌入汇编代码。2.autoauto(自动,automatic)是存储类型标识符,表明变量"自动"具有本地范围,块范围的变量声明(如for循环体内的......
  • 【C++】初识C++模板与STL
    C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C++内存管理本章将简单分享C++模板与STL相关知识,与之相关更多知识将留到下次更详细地来分享给大家......
  • vscode 编译c++项目如何配置
    配置c_cpp_properties.json文件主要用于辅助vscode智能代码提示、预定义编译宏定义示例如下:{"configurations":[{"name":"Win32","includePath":["${workspaceFolder}/**",......
  • 微软常用运行库合集|dll报错必装,Visual C++ 下载安装
    前言MicrosoftVisualC++Redistributable(简称MSVC,VB/VC,系统运行库)是Windows操作系统应用程序的基础类型库组件。此版VisualC++运行库组件合集(微软常用运行库合集)由国内封装爱好者@Dreamcast打包而成,整合VisualC++组件安装包运行库所有版本,提供图形安装界面,可自选更新V......
  • 算法与数据结构——内存与缓存
    内存与缓存数组和链表两种数据结构分别代表了“连续存储”和“分散存储”两种物理结构。实际上,物理结构在很大程度上决定了程序对内存和缓存的使用效率,进而影响算法程序的整体性能。计算机存储设备计算机中包括三种类型的存储设备:硬盘(harddisk)、内存(random-accessmemory,RAM)、......
  • C++学习随笔——简单的单例设计模式实例
    点击查看代码#include<iostream>classSingleton{private://私有化构造函数,防止外部实例化Singleton(){std::cout<<"SingletonInstanceCreated!"<<std::endl;}//删除拷贝构造函数和赋值运算符,防止拷贝实例Singleton(constSin......
  • vue element-ui表格table 表格动态 添加行、删除行、添加列、删除列 自定义表头
         vuetable表格动态添加行、删除行、添加列、删除列自定义表头; 增加一行、删除一行、添加一列、删除一列;每行带输入框input代码1、HTML部分:<template><divclass="app-container"><el-table:data="tableData"borderstyle="width:600px;margin-to......
  • ROS机器人入门系列(二)实现HelloWorld(c++/python)
    一、实现流程1、创建工作空间2、创建功能包3、编辑源文件4、编辑配置文件5、编译并执行其中,c++和python的差异仅体现在3,4两部,其他流程基本一致。二、创建工作空间和创建功能包的实现2.1 创建工作空间并初始化(1)创建工作空间mkdir-p自定义工作空间名称/src这里......
  • 突破编程:深入理解C++中的组合模式
    突破编程:深入理解C++中的组合模式在C++及众多面向对象编程语言中,设计模式是解决问题的经典方案,它们帮助开发者在面对复杂系统设计时,能够遵循一套经过验证的最佳实践。组合模式(CompositePattern)是这些设计模式中的一种,它提供了一种将对象组合成树形结构以表示“部分-整体”......
  • C++ 析构函数注意事项总结
    在C++中,析构函数是一个特殊的成员函数,它在对象生命周期结束时自动调用,用于执行清理工作,如释放分配给对象的内存、关闭文件、断开网络连接等。正确编写析构函数对于防止内存泄漏、资源泄露和其他资源管理问题至关重要。以下是编写C++析构函数时需要注意的一些重要事项:确保资......