一、分配器是什么?
分配器 = 负责给容器(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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论