본문으로 건너뛰기 C++ Custom Allocator | 'Custom Allocator' Guide

C++ Custom Allocator | 'Custom Allocator' Guide

C++ Custom Allocator | 'Custom Allocator' Guide

이 글의 핵심

template<typename T> class MyAllocator { public: using value_type = T; T allocate(size_t n) {.

What is Custom Allocator?

STL container memory management

template<typename T>
class MyAllocator {
public:
    using value_type = T;
    
    T* allocate(size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    void deallocate(T* p, size_t n) {
        ::operator delete(p);
    }
};

// Usage
std::vector<int, MyAllocator<int>> vec;

Basic Structure

template<typename T>
class Allocator {
public:
    using value_type = T;
    
    Allocator() = default;
    
    template<typename U>
    Allocator(const Allocator<U>&) {}
    
    T* allocate(size_t n) {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    void deallocate(T* p, size_t n) {
        ::operator delete(p);
    }
};

template<typename T, typename U>
bool operator==(const Allocator<T>&, const Allocator<U>&) {
    return true;
}

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

Practical Examples

Example 1: Logging Allocator

template<typename T>
class LoggingAllocator {
public:
    using value_type = T;
    
    T* allocate(size_t n) {
        std::cout << "Allocate: " << n << " * " << sizeof(T) << " bytes" << std::endl;
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    void deallocate(T* p, size_t n) {
        std::cout << "Deallocate: " << n << " * " << sizeof(T) << " bytes" << std::endl;
        ::operator delete(p);
    }
};

int main() {
    std::vector<int, LoggingAllocator<int>> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
}

Example 2: Pool Allocator

template<typename T>
class PoolAllocator {
    MemoryPool* pool;
    
public:
    using value_type = T;
    
    PoolAllocator(MemoryPool* p) : pool(p) {}
    
    template<typename U>
    PoolAllocator(const PoolAllocator<U>& other) : pool(other.pool) {}
    
    T* allocate(size_t n) {
        if (n != 1) {
            throw std::bad_alloc();
        }
        return static_cast<T*>(pool->allocate());
    }
    
    void deallocate(T* p, size_t n) {
        pool->deallocate(p);
    }
    
    template<typename U>
    friend class PoolAllocator;
};

int main() {
    MemoryPool pool(sizeof(int), 100);
    std::vector<int, PoolAllocator<int>> vec{&pool};
}

Example 3: Aligned Allocator

template<typename T, size_t Alignment = 64>
class AlignedAllocator {
public:
    using value_type = T;
    
    T* allocate(size_t n) {
        void* ptr = aligned_alloc(Alignment, n * sizeof(T));
        if (!ptr) {
            throw std::bad_alloc();
        }
        return static_cast<T*>(ptr);
    }
    
    void deallocate(T* p, size_t n) {
        free(p);
    }
};

int main() {
    // 64-byte alignment
    std::vector<float, AlignedAllocator<float, 64>> vec;
}

Example 4: Statistics Allocator

template<typename T>
class StatsAllocator {
    static inline size_t allocCount = 0;
    static inline size_t deallocCount = 0;
    static inline size_t bytesAllocated = 0;
    
public:
    using value_type = T;
    
    T* allocate(size_t n) {
        ++allocCount;
        bytesAllocated += n * sizeof(T);
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }
    
    void deallocate(T* p, size_t n) {
        ++deallocCount;
        ::operator delete(p);
    }
    
    static void printStats() {
        std::cout << "Allocations: " << allocCount << std::endl;
        std::cout << "Deallocations: " << deallocCount << std::endl;
        std::cout << "Bytes: " << bytesAllocated << std::endl;
    }
};

Allocator Requirements

template<typename T>
class Allocator {
public:
    // Required
    using value_type = T;
    
    T* allocate(size_t n);
    void deallocate(T* p, size_t n);
    
    // Optional
    template<typename U>
    Allocator(const Allocator<U>&);
    
    // Comparison
    bool operator==(const Allocator&) const;
    bool operator!=(const Allocator&) const;
};

Summary

Key Points

  1. Custom allocator: Customize STL container memory management
  2. Basic structure: value_type, allocate(), deallocate()
  3. Use cases: Logging, pool, alignment, statistics
  4. Requirements: Must implement required interface

When to Use

Use custom allocator when:

  • Need memory pool
  • Need aligned allocation
  • Need allocation tracking/profiling
  • Need custom memory management

Don’t use when:

  • Default allocator is sufficient
  • Adds unnecessary complexity
  • Performance gain is negligible

Best Practices

  • ✅ Implement required interface correctly
  • ✅ Handle exceptions properly
  • ✅ Consider allocator equality
  • ❌ Don’t make allocators too complex
  • ❌ Don’t forget thread safety if needed

Master custom allocators for efficient memory management! 🚀


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Development blog post organizing C++ Custom Allocator.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, allocator, memory, STL, custom.