CppCon 2017: Bob Steagall “How to Write a Custom Allocator”
Table of Contents
Introduction
This tutorial will guide you through the process of writing a custom allocator in C++. Custom allocators can significantly enhance the performance of your application by optimizing memory management. This guide will cover the requirements and best practices for implementing custom allocators in accordance with C++14 and C++17 standards.
Step 1: Understand the Allocator Model
Before you begin writing a custom allocator, familiarize yourself with the standard allocator model.
-
Standard Requirements: Review the allocator requirements specified in the C++ standard. A custom allocator must fulfill certain interface requirements, including:
allocate(size_t n)
deallocate(T* p, size_t n)
construct(T* p, Args&&... args)
destroy(T* p)
-
Allocator Traits: Understand
std::allocator_traits
, which helps in creating custom allocators. This template provides a way to define allocator properties and operations.
Step 2: Implement Basic Allocator Functions
Start implementing the basic functions required by your custom allocator.
-
Allocate Function: This function allocates memory for n objects.
template <typename T> T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
-
Deallocate Function: This function frees the allocated memory.
template <typename T> void deallocate(T* p, size_t n) { ::operator delete(p); }
-
Construct and Destroy Functions: These functions manage object construction and destruction.
template <typename T, typename... Args> void construct(T* p, Args&&... args) { new(p) T(std::forward<Args>(args)...); } template <typename T> void destroy(T* p) { p->~T(); }
Step 3: Handle Allocator Identity and Propagation
Understand how allocators propagate and their identity:
-
Allocator Identity: Each allocator should be unique to avoid unexpected behavior. Implement the equality and inequality operators.
-
Propagation: Learn how allocators are passed between containers. When a container is copied or moved, the allocator must also be copied or moved accordingly.
Step 4: Define Custom Allocator Semantics
Consider implementing unique semantics for your custom allocator:
-
Stateful Allocators: Your allocator can maintain state, which can be beneficial depending on your application needs. Ensure that the equality operator reflects the allocator's state correctly.
-
Non-equal Instances: Handle instances of allocators that may not compare equal, impacting container behavior.
Step 5: Specify the Public Interface
Detail the public interface of your custom allocator, ensuring it is intuitive and follows the expected conventions:
-
Interface Design: Provide clear and concise methods for allocation, deallocation, construction, and destruction.
-
Documentation: Comment your code and provide usage examples to help users understand how to utilize your allocator effectively.
Conclusion
In this tutorial, you learned the fundamentals of writing a custom allocator in C++. Key takeaways include understanding the standard allocator model, implementing basic functions, managing allocator identity, providing unique semantics, and designing a user-friendly interface.
As a next step, consider testing your custom allocator with various standard containers and profiling its performance against the default allocator to see the benefits in action.