C++ Allocator | Custom allocators for STL containers
이 글의 핵심
When and how to customize allocation: traits, vector with MyAllocator, pool examples, PMR, and debugging with tracking allocators.
Default allocator
#include <memory>
// 기본 할당자
allocator<int> alloc;
// 메모리 할당
int* ptr = alloc.allocate(10); // 10개 int 공간
// 객체 생성
alloc.construct(ptr, 42);
// 객체 파괴
alloc.destroy(ptr);
// 메모리 해제
alloc.deallocate(ptr, 10);
```## Container and Allocator```cpp
// 기본 할당자
vector<int> v1;
// 커스텀 할당자
vector<int, MyAllocator<int>> v2;
// 할당자 전달
MyAllocator<int> alloc;
vector<int, MyAllocator<int>> v3(alloc);
```## Custom Allocator implementation```cpp
template<typename T>
class MyAllocator {
public:
using value_type = T;
MyAllocator() noexcept {}
template<typename U>
MyAllocator(const MyAllocator<U>&) noexcept {}
T* allocate(size_t n) {
cout << "할당: " << n << "개" << endl;
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* ptr, size_t n) noexcept {
cout << "해제: " << n << "개" << endl;
::operator delete(ptr);
}
};
template<typename T, typename U>
bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) {
return true;
}
template<typename T, typename U>
bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) {
return false;
}
int main() {
vector<int, MyAllocator<int>> v;
v.push_back(1); // 할당 발생
v.push_back(2);
v.push_back(3);
}
```## Practical example
### Example 1: Memory Pool Allocator```cpp
template<typename T>
class PoolAllocator {
private:
struct Block {
T data;
Block* next;
};
Block* freeList = nullptr;
vector<Block*> pools;
static constexpr size_t POOL_SIZE = 1024;
void allocatePool() {
Block* pool = static_cast<Block*>(::operator new(POOL_SIZE * sizeof(Block)));
pools.push_back(pool);
for (size_t i = 0; i < POOL_SIZE - 1; i++) {
pool[i].next = &pool[i + 1];
}
pool[POOL_SIZE - 1].next = nullptr;
freeList = pool;
}
public:
using value_type = T;
PoolAllocator() {
allocatePool();
}
~PoolAllocator() {
for (Block* pool : pools) {
::operator delete(pool);
}
}
T* allocate(size_t n) {
if (n != 1) {
throw bad_alloc();
}
if (!freeList) {
allocatePool();
}
Block* block = freeList;
freeList = freeList->next;
return &block->data;
}
void deallocate(T* ptr, size_t n) noexcept {
if (n != 1) return;
Block* block = reinterpret_cast<Block*>(ptr);
block->next = freeList;
freeList = block;
}
};
int main() {
list<int, PoolAllocator<int>> myList;
for (int i = 0; i < 1000; i++) {
myList.push_back(i); // 풀에서 할당
}
}
```### Example 2: Stack allocator```cpp
template<typename T, size_t N>
class StackAllocator {
private:
alignas(T) char buffer[N * sizeof(T)];
size_t used = 0;
public:
using value_type = T;
T* allocate(size_t n) {
if (used + n > N) {
throw bad_alloc();
}
T* result = reinterpret_cast<T*>(buffer + used * sizeof(T));
used += n;
return result;
}
void deallocate(T* ptr, size_t n) noexcept {
// 스택 할당자는 해제 안함 (스코프 종료 시 자동)
}
};
int main() {
// 스택에 할당
vector<int, StackAllocator<int, 100>> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// 스코프 종료 시 자동 해제
}
```### Example 3: Trace allocator```cpp
template<typename T>
class TrackingAllocator {
private:
static size_t allocCount;
static size_t deallocCount;
static size_t bytesAllocated;
public:
using value_type = T;
T* allocate(size_t n) {
allocCount++;
bytesAllocated += n * sizeof(T);
cout << "할당: " << n * sizeof(T) << " bytes" << endl;
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* ptr, size_t n) noexcept {
deallocCount++;
bytesAllocated -= n * sizeof(T);
cout << "해제: " << n * sizeof(T) << " bytes" << endl;
::operator delete(ptr);
}
static void printStats() {
cout << "할당 횟수: " << allocCount << endl;
cout << "해제 횟수: " << deallocCount << endl;
cout << "현재 사용: " << bytesAllocated << " bytes" << endl;
}
};
template<typename T>
size_t TrackingAllocator<T>::allocCount = 0;
template<typename T>
size_t TrackingAllocator<T>::deallocCount = 0;
template<typename T>
size_t TrackingAllocator<T>::bytesAllocated = 0;
int main() {
{
vector<int, TrackingAllocator<int>> v;
for (int i = 0; i < 100; i++) {
v.push_back(i);
}
}
TrackingAllocator<int>::printStats();
}
PMR (Polymorphic Memory Resources)
#include <memory_resource>
int main() {
// 단조 버퍼
char buffer[1024];
pmr::monotonic_buffer_resource pool(buffer, sizeof(buffer));
// PMR 벡터
pmr::vector<int> v(&pool);
for (int i = 0; i < 100; i++) {
v.push_back(i); // buffer에서 할당
}
}
```## Frequently occurring problems
### Issue 1: Allocator Comparison```cpp
// ❌ 할당자 비교 안함
template<typename T>
class BadAllocator {
// operator== 없음
};
// ✅ 할당자 비교 구현
template<typename T>
class GoodAllocator {
// ...
};
template<typename T, typename U>
bool operator==(const GoodAllocator<T>&, const GoodAllocator<U>&) {
return true;
}
```### Issue 2: Allocator Propagation```cpp
// 컨테이너 복사 시 할당자 전파 여부
template<typename T>
struct allocator_traits<MyAllocator<T>> {
using propagate_on_container_copy_assignment = true_type;
using propagate_on_container_move_assignment = true_type;
using propagate_on_container_swap = true_type;
};
```### Issue 3: Misaligned memory```cpp
// ❌ 정렬 고려 안함
char buffer[100];
int* ptr = reinterpret_cast<int*>(buffer); // 정렬 안됨!
// ✅ alignas 사용
alignas(int) char buffer[100];
int* ptr = reinterpret_cast<int*>(buffer);
```## FAQ
### Q1: When do I use a custom allocator?
**A**:
- memory pool
- Special memory area (GPU, shared memory)
- Memory tracing/debugging
- Performance optimization
### Q2: What are the performance benefits?
**A**: Allocation/deallocation can be 10-100x faster when using memory pools.
### Q3: What is PMR?
**A**: Polymorphic memory resources in C++17. You can change the allocator at runtime.
### Q4: Is it difficult to implement an allocator?
**A**: The basic implementation is simple, but the complete implementation is complex. Use allocator_traits.
### Q5: What about allocator debugging?
**A**:
- Use trace allocator
-Valgrind
-AddressSanitizer
### Q6: What are Allocator learning resources?
**A**:
- cppreference.com
- "The C++ Standard Library" (Nicolai Josuttis)
- Boost.Pool source code
---
## Good article to read together (internal link)
Here's another article related to this topic.
- [C++ Custom Allocator | “Custom Allocator” Guide](/blog/cpp-custom-allocator/)
- [C++ PMR | "Polymorphic Memory Resources" Guide](/blog/cpp-pmr/)
- [C++ Stack Allocator | “Stack Allocator” Guide](/blog/cpp-stack-allocator/)
## Practical tips
These are tips that can be applied right away in practice.
### Debugging tips
- If you run into a problem, check the compiler warnings first.
- Reproduce the problem with a simple test case
### Performance Tips
- Don't optimize without profiling
- Set measurable indicators first
### Code review tips
- Check in advance for areas that are frequently pointed out in code reviews.
- Follow your team's coding conventions
---
## Practical checklist
This is what you need to check when applying this concept in practice.
### Before writing code
- [ ] Is this technique the best way to solve the current problem?
- [ ] Can team members understand and maintain this code?
- [ ] Does it meet the performance requirements?
### Writing code
- [ ] Have you resolved all compiler warnings?
- [ ] Have you considered edge cases?
- [ ] Is error handling appropriate?
### When reviewing code
- [ ] Is the intent of the code clear?
- [ ] Are there enough test cases?
- [ ] Is it documented?
Use this checklist to reduce mistakes and improve code quality.
---
## Keywords covered in this article (related search terms)
This article will be helpful if you search for C++, allocator, memory, STL, PMR, etc.
---
## Related articles
- [C++ vector reserve vs resize | ](/blog/cpp-comparison-09-vector-reserve-resize/)
- [C++ Custom Allocator | ](/blog/cpp-custom-allocator/)
- [C++ Algorithm Copy | ](/blog/cpp-algorithm-copy/)
- [C++ Algorithm Count | ](/blog/cpp-algorithm-count/)
- [C++ Algorithm Generate | ](/blog/cpp-algorithm-generate/)