C++中分配器allocator的实现

 更新时间:2026年03月08日 15:49:44   作者:yy__xzz  
本文主要介绍了C++中分配器allocator的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、分配器是什么?

分配器 = 负责给容器(vector、string等)分配内存的"后勤部长"

现实类比

容器(vector)就像一家餐厅:
- 餐厅需要食材(内存)
- 默认情况下,餐厅自己每天去市场采购(new/delete)
- 分配器就是"采购员":可以换不同的采购员,有的高效,有的省钱

二、为什么要学分配器?(三个层次)

层次1:根本不用管(日常开发)

vector<int> v;  // 就这样用,完全不用管分配器
v.push_back(10);
// 默认分配器工作得很好

层次2:知道有这东西(面试够用)

// vector其实有两个模板参数
vector<int> v;  // 等价于:
vector<int, allocator<int>> v;  // 第二个参数就是分配器

层次3:想玩一玩

学会使用和观察分配器,理解内存分配过程

三、先看默认分配器怎么工作

观察vector的内存分配

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v;
    
    cout << "初始容量: " << v.capacity() << endl;
    
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
        cout << "添加 " << i << " 后,容量: " << v.capacity() << endl;
    }
    
    return 0;
}

输出:

初始容量: 0
添加 0 后,容量: 1
添加 1 后,容量: 2
添加 2 后,容量: 4
添加 3 后,容量: 4
添加 4 后,容量: 8
...

vector会自动扩容,这些都是分配器在幕后工作。

四、自己动手:最简单的自定义分配器

步骤1:写一个打印日志的分配器

#include <iostream>
#include <vector>
using namespace std;

// 一个超级简单的分配器,只做一件事:打印日志
template<typename T>
class LogAllocator {
public:
    // 必须的类型定义(STL容器需要)
    using value_type = T;
    
    // 构造函数(可以为空)
    LogAllocator() = default;
    
    // 模板构造函数(用于分配器之间的转换)
    template<typename U>
    LogAllocator(const LogAllocator<U>&) {}
    
    // 最重要的函数:分配内存
    T* allocate(size_t n) {
        cout << "【分配器】分配 " << n << " 个元素,大小: " 
             << n * sizeof(T) << " 字节" << endl;
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    // 释放内存
    void deallocate(T* p, size_t n) {
        cout << "【分配器】释放 " << n << " 个元素,地址: " << p << endl;
        ::operator delete(p);
    }
};

// 为了让不同元素类型的分配器可以互相转换
template<typename T, typename U>
bool operator==(const LogAllocator<T>&, const LogAllocator<U>&) {
    return true;
}

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

步骤2:使用自定义分配器

int main() {
    // 使用自定义分配器的vector
    vector<int, LogAllocator<int>> v;
    
    cout << "开始添加元素..." << endl;
    for (int i = 0; i < 5; i++) {
        v.push_back(i * 10);
    }
    
    cout << "vector内容: ";
    for (int x : v) cout << x << " ";
    cout << endl;
    
    cout << "程序结束,vector销毁时会自动释放内存" << endl;
    return 0;
}

输出:

开始添加元素...
【分配器】分配 1 个元素,大小: 4 字节
【分配器】分配 2 个元素,大小: 8 字节
【分配器】释放 1 个元素,地址: 0x...
【分配器】分配 4 个元素,大小: 16 字节
【分配器】释放 2 个元素,地址: 0x...
vector内容: 0 10 20 30 40 
程序结束,vector销毁时会自动释放内存
【分配器】释放 4 个元素,地址: 0x...

可以清楚地看到vector什么时候分配、释放内存!

五、更实用的例子:统计内存使用

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// 统计内存使用量的分配器
template<typename T>
class StatsAllocator {
private:
    static size_t totalAllocated;  // 总共分配了多少
    static size_t totalFreed;      // 总共释放了多少
    static size_t currentUsage;    // 当前使用量
    
public:
    using value_type = T;
    
    StatsAllocator() = default;
    
    template<typename U>
    StatsAllocator(const StatsAllocator<U>&) {}
    
    T* allocate(size_t n) {
        size_t bytes = n * sizeof(T);
        totalAllocated += bytes;
        currentUsage += bytes;
        
        cout << "【分配】" << bytes << " 字节 (当前总使用: " 
             << currentUsage << " 字节)" << endl;
        
        return static_cast<T*>(::operator new(bytes));
    }
    
    void deallocate(T* p, size_t n) {
        size_t bytes = n * sizeof(T);
        totalFreed += bytes;
        currentUsage -= bytes;
        
        cout << "【释放】" << bytes << " 字节 (当前总使用: " 
             << currentUsage << " 字节)" << endl;
        
        ::operator delete(p);
    }
    
    // 静态方法查看统计信息
    static void printStats() {
        cout << "\n=== 内存统计 ===" << endl;
        cout << "总共分配: " << totalAllocated << " 字节" << endl;
        cout << "总共释放: " << totalFreed << " 字节" << endl;
        cout << "当前使用: " << currentUsage << " 字节" << endl;
        cout << "================" << endl;
    }
};

// 初始化静态成员
template<typename T>
size_t StatsAllocator<T>::totalAllocated = 0;
template<typename T>
size_t StatsAllocator<T>::totalFreed = 0;
template<typename T>
size_t StatsAllocator<T>::currentUsage = 0;

// 定义比较操作
template<typename T, typename U>
bool operator==(const StatsAllocator<T>&, const StatsAllocator<U>&) {
    return true;
}

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

// 使用统计分配器的vector
template<typename T>
using StatsVector = vector<T, StatsAllocator<T>>;

int main() {
    cout << "=== 测试1: vector<int> ===" << endl;
    {
        StatsVector<int> v;
        for (int i = 0; i < 10; i++) {
            v.push_back(i);
        }
    }  // v销毁,释放内存
    StatsAllocator<int>::printStats();
    
    cout << "\n=== 测试2: vector<string> ===" << endl;
    {
        StatsVector<string> v;
        v.push_back("hello");
        v.push_back("world");
        v.push_back("C++");
        v.push_back("allocator");
    }
    StatsAllocator<string>::printStats();
    
    return 0;
}

六、分配器使用总结

什么时候用分配器?

场景是否需要例子
写业务代码❌ 不需要网站后端、APP开发
写基础库✅ 可能需要STL、Qt、Boost
性能调优✅ 可以考虑游戏服务器、高频交易
调试内存✅ 很有用找内存泄漏
面试学习✅ 了解概念知道原理即可

到此这篇关于C++中分配器allocator的实现的文章就介绍到这了,更多相关C++ 分配器allocator内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++锁机制与信号机制对比分析

    C++锁机制与信号机制对比分析

    文章介绍C++多线程编程中线程同步与通信的锁机制(互斥锁、读写锁及RAII封装)和信号机制(条件变量、信号量),并对比其在资源控制、并发限制和线程协调中的应用场景与优缺点,对C++锁机制与信号机制相关知识感兴趣的朋友一起看看吧
    2025-06-06
  • boost.asio框架系列之socket编程

    boost.asio框架系列之socket编程

    这篇文章介绍了boost.asio框架系列之socket编程,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C语言求Fibonacci斐波那契数列通项问题的解法总结

    C语言求Fibonacci斐波那契数列通项问题的解法总结

    斐波那契数列相关问题是考研和ACM中常见的算法题目,这里特地为大家整理了C语言求Fibonacci斐波那契数列通项问题的解法总结,需要的朋友可以参考下
    2016-06-06
  • C语言指针用法总结

    C语言指针用法总结

    本文详细讲解了C语言指针用法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Qt 编译配置 Protobuf 的详细步骤

    Qt 编译配置 Protobuf 的详细步骤

    在Qt项目中使用Protobuf(Protocol Buffers)可以有效地处理数据序列化和反序列化,以下是如何在Qt项目中配置和编译Protobuf的详细步骤,感兴趣的朋友一起看看吧
    2024-07-07
  • C语言系列之推箱子游戏

    C语言系列之推箱子游戏

    这篇文章主要为大家详细介绍了C语言系列之推箱子游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • c++基础语法:构造函数与析构函数

    c++基础语法:构造函数与析构函数

    构造函数用来构造一个对象,主要完成一些初始化工作,如果类中不提供构造函数,编译器会默认的提供一个默认构造函数(参数为空的构造函数就是默认构造函数) ;析构函数是隐式调用的,delete对象时候会自动调用完成对象的清理工作
    2013-09-09
  • C++ 数据结构二叉树(前序/中序/后序递归、非递归遍历)

    C++ 数据结构二叉树(前序/中序/后序递归、非递归遍历)

    这篇文章主要介绍了C++ 数据结构二叉树(前序/中序/后序递归、非递归遍历)的相关资料,这里提供实例代码来帮助大家理解掌握二叉树,需要的朋友可以参考下
    2017-07-07
  • c++重载运算符时返回值为类的对象或者返回对象的引用问题

    c++重载运算符时返回值为类的对象或者返回对象的引用问题

    这篇文章主要介绍了c++重载运算符时返回值为类的对象或者返回对象的引用问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C++高级数据结构之二叉查找树

    C++高级数据结构之二叉查找树

    这篇文章主要介绍了C++高级数据结构之二叉查找树,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-05-05

最新评论