C++ Allocator | Custom allocators for STL containers

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/)