samedi 26 novembre 2016

C++11 Allocator

I have not seen many good examples or tutorials on how to create a custom allocator in C++11. I do see that it has changed since C++03. However, I didn't know how to do it then either.

I've got the following that I threw together via as much information as I could find:

// Project Includes
#include "IMemoryManager.h"

// Standard Includes
#include <cstddef>
#include <iostream>
#include <memory>

//------------------------------------------------------------------------------
/// <summary>
/// A STL compliant custom allocator
///
/// Based on Dr Dobbs article
/// http://ift.tt/1nfXakE
/// I attempted to make it C++11 compliant.
/// Takes an Interface to a Memory Manager which will implement the custom memory management solution.
/// </summary>
template <class T>
class CustomAllocator
{
    std::unique_ptr<IMemoryManager> m_memoryManager;

public:

    typedef T value_type;

    /// Allocator takes ownership of memory manager
    CustomAllocator(std::unique_ptr<IMemoryManager> memoryManager)
        :
        m_memoryManager(std::move(memoryManager))
    {
    }

    ~CustomAllocator()
    {
    }

    CustomAllocator(const CustomAllocator & rhs) = delete;
    void operator =(const CustomAllocator & rhs) = delete;

    bool operator == (const CustomAllocator<T> & rhs)
    {
        return true;
    }

    bool operator != (const CustomAllocator<T> & rhs)
    {
        return false;
    }

    template <class U>
    CustomAllocator(const CustomAllocator<U> & rhs)
        :
        m_memoryManager(std::move(rhs.m_memoryManager))
    {
    }

    T * allocate(size_t n)
    {
        const size_t numBytes = n * sizeof(T);
        void * p = m_memoryManager->allocate(numBytes);

        if (!p)
        {
            throw std::bad_alloc();
        }

        std::cout << "Allocated " << numBytes << " bytes for a " << typeid(T).name() << std::endl;
        return static_cast<T *>(p);
    }

    void deallocate(T * p, size_t numObjects)
    {
        const size_t numBytes = sizeof(*p);

        m_memoryManager->free(p);
        std::cout << "Deallocated " << numBytes << " bytes for a " << typeid(T).name() << std::endl;
    }

    void construct(T * p, const T & x)
    {
        // 'Placement new' constructs an object at a specified location
        // The custom allocator seperates allocation from construction
        new(p) T(x);
    }

    void destroy(T * p)
    {
        p->~T();
    }
};

I've got several questions about it. 1) How do I do the operator == and operator != not that it has a member variable that is a unique pointer? Do I just compare the address of the memory manager? Why does the standard ask me to implement comparison operators anyway if there is only supposed to be one of these and it is not copyable? Or does it?

2) Is it safe to move the memory manager in the copy constructor for the type U? I assume when the STL containers make a new allocator for a different type, it no longer uses the old. Is that correct?

3) I did not see construct or destroy methods anymore at http://ift.tt/2fBWKd2 Do I still need to implement those?

4) How do I pass the argument to the Allocator's constuctor when I use it as an argument for an STL container?

void RunCustomAllocator()
{
    // Does not compile
    std::vector<ComplexNumber, CustomAllocator<ComplexNumber> > myVector;
    ComplexNumber number(1, 1);
    myVector.push_back(number);
}

Am I barking up the right tree here? Is this how we do custom memory management these days?

Aucun commentaire:

Enregistrer un commentaire