C++中分配器allocator的實(shí)現(xiàn)
一、分配器是什么?
分配器 = 負(fù)責(zé)給容器(vector、string等)分配內(nèi)存的"后勤部長(zhǎng)"
現(xiàn)實(shí)類比
容器(vector)就像一家餐廳:
- 餐廳需要食材(內(nèi)存)
- 默認(rèn)情況下,餐廳自己每天去市場(chǎng)采購(new/delete)
- 分配器就是"采購員":可以換不同的采購員,有的高效,有的省錢
二、為什么要學(xué)分配器?(三個(gè)層次)
層次1:根本不用管(日常開發(fā))
vector<int> v; // 就這樣用,完全不用管分配器 v.push_back(10); // 默認(rèn)分配器工作得很好
層次2:知道有這東西(面試夠用)
// vector其實(shí)有兩個(gè)模板參數(shù) vector<int> v; // 等價(jià)于: vector<int, allocator<int>> v; // 第二個(gè)參數(shù)就是分配器
層次3:想玩一玩
學(xué)會(huì)使用和觀察分配器,理解內(nèi)存分配過程
三、先看默認(rèn)分配器怎么工作
觀察vector的內(nèi)存分配
#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會(huì)自動(dòng)擴(kuò)容,這些都是分配器在幕后工作。
四、自己動(dòng)手:最簡(jiǎn)單的自定義分配器
步驟1:寫一個(gè)打印日志的分配器
#include <iostream>
#include <vector>
using namespace std;
// 一個(gè)超級(jí)簡(jiǎn)單的分配器,只做一件事:打印日志
template<typename T>
class LogAllocator {
public:
// 必須的類型定義(STL容器需要)
using value_type = T;
// 構(gòu)造函數(shù)(可以為空)
LogAllocator() = default;
// 模板構(gòu)造函數(shù)(用于分配器之間的轉(zhuǎn)換)
template<typename U>
LogAllocator(const LogAllocator<U>&) {}
// 最重要的函數(shù):分配內(nèi)存
T* allocate(size_t n) {
cout << "【分配器】分配 " << n << " 個(gè)元素,大小: "
<< n * sizeof(T) << " 字節(jié)" << endl;
return static_cast<T*>(::operator new(n * sizeof(T)));
}
// 釋放內(nèi)存
void deallocate(T* p, size_t n) {
cout << "【分配器】釋放 " << n << " 個(gè)元素,地址: " << p << endl;
::operator delete(p);
}
};
// 為了讓不同元素類型的分配器可以互相轉(zhuǎn)換
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內(nèi)容: ";
for (int x : v) cout << x << " ";
cout << endl;
cout << "程序結(jié)束,vector銷毀時(shí)會(huì)自動(dòng)釋放內(nèi)存" << endl;
return 0;
}
輸出:
開始添加元素...
【分配器】分配 1 個(gè)元素,大小: 4 字節(jié)
【分配器】分配 2 個(gè)元素,大小: 8 字節(jié)
【分配器】釋放 1 個(gè)元素,地址: 0x...
【分配器】分配 4 個(gè)元素,大小: 16 字節(jié)
【分配器】釋放 2 個(gè)元素,地址: 0x...
vector內(nèi)容: 0 10 20 30 40
程序結(jié)束,vector銷毀時(shí)會(huì)自動(dòng)釋放內(nèi)存
【分配器】釋放 4 個(gè)元素,地址: 0x...
可以清楚地看到vector什么時(shí)候分配、釋放內(nèi)存!
五、更實(shí)用的例子:統(tǒng)計(jì)內(nèi)存使用
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 統(tǒng)計(jì)內(nèi)存使用量的分配器
template<typename T>
class StatsAllocator {
private:
static size_t totalAllocated; // 總共分配了多少
static size_t totalFreed; // 總共釋放了多少
static size_t currentUsage; // 當(dāng)前使用量
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 << " 字節(jié) (當(dāng)前總使用: "
<< currentUsage << " 字節(jié))" << 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 << " 字節(jié) (當(dāng)前總使用: "
<< currentUsage << " 字節(jié))" << endl;
::operator delete(p);
}
// 靜態(tài)方法查看統(tǒng)計(jì)信息
static void printStats() {
cout << "\n=== 內(nèi)存統(tǒng)計(jì) ===" << endl;
cout << "總共分配: " << totalAllocated << " 字節(jié)" << endl;
cout << "總共釋放: " << totalFreed << " 字節(jié)" << endl;
cout << "當(dāng)前使用: " << currentUsage << " 字節(jié)" << endl;
cout << "================" << endl;
}
};
// 初始化靜態(tài)成員
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;
}
// 使用統(tǒng)計(jì)分配器的vector
template<typename T>
using StatsVector = vector<T, StatsAllocator<T>>;
int main() {
cout << "=== 測(cè)試1: vector<int> ===" << endl;
{
StatsVector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
} // v銷毀,釋放內(nèi)存
StatsAllocator<int>::printStats();
cout << "\n=== 測(cè)試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;
}
六、分配器使用總結(jié)
什么時(shí)候用分配器?
| 場(chǎng)景 | 是否需要 | 例子 |
|---|---|---|
| 寫業(yè)務(wù)代碼 | ? 不需要 | 網(wǎng)站后端、APP開發(fā) |
| 寫基礎(chǔ)庫 | ? 可能需要 | STL、Qt、Boost |
| 性能調(diào)優(yōu) | ? 可以考慮 | 游戲服務(wù)器、高頻交易 |
| 調(diào)試內(nèi)存 | ? 很有用 | 找內(nèi)存泄漏 |
| 面試學(xué)習(xí) | ? 了解概念 | 知道原理即可 |
到此這篇關(guān)于C++中分配器allocator的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 分配器allocator內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言實(shí)現(xiàn)簡(jiǎn)單電子通訊錄(2)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡(jiǎn)單電子通訊錄的第二部分,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
簡(jiǎn)單分析C語言中指針數(shù)組與數(shù)組指針的區(qū)別
這篇文章主要介紹了C語言中指針數(shù)組與數(shù)組指針的區(qū)別,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11
C語言中pow函數(shù)使用方法、注意事項(xiàng)以及常見報(bào)錯(cuò)原因
在c語言當(dāng)中我們要計(jì)算一個(gè)數(shù)的n次方時(shí)候,可以使用多種方法,但是也有一種比較簡(jiǎn)單的方法,便是調(diào)用一個(gè)函數(shù)pow函數(shù),下面這篇文章主要給大家介紹了關(guān)于C語言中pow函數(shù)使用方法、注意事項(xiàng)以及常見報(bào)錯(cuò)原因的相關(guān)資料,需要的朋友可以參考下2022-11-11
手動(dòng)添加bits/stdc++.h到vs2017的詳細(xì)步驟
這篇文章主要介紹了手動(dòng)添加bits/stdc++.h到vs2017的詳細(xì)步驟,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
C++?LeetCode1769移動(dòng)所有球到每個(gè)盒子最小操作數(shù)示例
這篇文章主要為大家介紹了C++?LeetCode1769移動(dòng)所有球到每個(gè)盒子所需最小操作數(shù)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Visual Studio 2022最新版安裝教程(圖文詳解)
本文主要介紹了Visual Studio 2022最新版安裝教程,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
深入剖析C++中的struct結(jié)構(gòu)體字節(jié)對(duì)齊
要求數(shù)據(jù)內(nèi)存的起始地址的值是某個(gè)數(shù)k的倍數(shù),這就是所謂的內(nèi)存對(duì)齊,本文就來深入剖析C++中的struct結(jié)構(gòu)體字節(jié)對(duì)齊,需要的朋友可以參考下2016-05-05

