简体   繁体   中英

Writing a custom allocator

I'm trying to write a custom allocator, which preallocates space for a fixed number of elements. However, I have some problems with understanding the requirements.

allocator.h

#pragma once
#ifndef _ALLOCATOR_H
#define _ALLOCATOR_H

template<typename T>
class Allocator
{
public:
    //    typedefs
    typedef T value_type;
    typedef value_type* pointer;
    typedef const value_type* const_pointer;
    typedef value_type& reference;
    typedef const value_type& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;

public:
    //    convert an allocator<T> to allocator<U>
    template<typename U>
    struct rebind
    {
        typedef Allocator<U> other;
    };

public:
    explicit Allocator(void)
    {
        mCurElement = 0;
        mMaxElements = 650000000;
        mBase = reinterpret_cast<pointer>(::operator new(mMaxElements * sizeof(T)));
    }

    virtual ~Allocator(void)
    {
        ::operator delete(mBase);
    }

    explicit Allocator(Allocator const &oOther)
    {
        mCurElement = oOther.mCurElement;
        mMaxElements = oOther.mMaxElements;
        mBase = oOther.mBase;
    }

    template<typename U>
    explicit Allocator(Allocator<U> const &oOther)
    {
        mCurElement = 0;
        mMaxElements = 650000000;
        mBase = oOther.mBase;
    }

    //    address
    pointer address(reference r) { return &r; }
    const_pointer address(const_reference r) { return &r; }

    //    memory allocation
    pointer allocate(size_type nElements, typename std::allocator<void>::const_pointer = 0)
    {
        if (mCurElement > mMaxElements)
            return NULL;

        //pointer p = reinterpret_cast<pointer>(::operator new(cnt * sizeof(T)));
        pointer p = &mBase[mCurElement];
        mCurElement += nElements;
        return p;
    }
    void deallocate(pointer pAddress, size_type)
    {
        //::operator delete(pAddress);
        mCurElement--;
    }

    //    size
    size_type max_size() const
    {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    //    construction/destruction
    void construct(pointer pAddress, const T& oObject)
    {
        new(pAddress) T(oObject);
    }
    void destroy(pointer pAddress)
    {
        pAddress->~T();
    }

    bool operator==(Allocator const&) { return true; }
    bool operator!=(Allocator const& oAllocator) { return !operator==(oAllocator); }

public:
    T *getBase(void) const { return mBase; }

private:
    static usize_t mId;
    T *mBase;
    usize_t mMaxElements;
    usize_t mCurElement;
};


#endif // _ALLOCATOR_H

allocator.cpp

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <sstream>
#include <set>
#include <ctime>

#include "allocator.h"

typedef unsigned int uint_t;
typedef unsigned long long usize_t;
usize_t Allocator<usize_t>::mId;

void testStdAllocator(usize_t nIterations, usize_t nMaxValue)
{
    std::set<usize_t, std::less<usize_t>, Allocator<usize_t>> st;
    std::string id = "Standard Set";

    clock_t start = clock();
    for (usize_t i = 0; i < nIterations; i++)
    {
        usize_t val = (usize_t)(rand() % nMaxValue) + 1;
        if (i % 1000000 == 0)
            std::cout << id << " testing ... " << i << "/" << nIterations << "\r";

        st.insert(val);
    }

    std::cout << id << " Elapsed: " << clock() - start << std::endl;
}

int main(int argc, char *argv[])
{
    usize_t iterations =    650000000;
    usize_t val =           6500000;

    std::cout << "Allocator" << std::endl;

    testStdAllocator(iterations, val);

    return 0;
}

The problem I have is:

Why do I need the template <typename U> ... ? (I found an example and adpated it)

When I made it compilable and tested it the std::set apparently creates copies of the allocator, so I would have to pass around the pointer. I can use an std::shared_ptr for that, but I don't really see why this should be needed in the first place.

Apparently there is something about proxied containers where the template <typename U> is needed for, but this again creates the additional problem of passing the pointer around for an (apparently) different allocator type.

So I would appreciate some pointers where I'm going wrong.

When you pass an allocator to std::set<T, C A> it is meant to have an allocate() function allcoating space for T objects. However, the std::set<T, C, A> will not allocate any T object. It will, instead, allocate _Node<T> objects where _Node is some tree node representation capable of holding T objects but also containing suitable pointers to other nodes.

To allocate an object of _Node<T> an allocator based on A is needed. This allocator's type is obtained from A::rebind<_Node<T>>::other and initialized appropriately by passing the original allocator object (or an object created from that) as constructor argument.

Of course, using stateful allocators does assume that you use the C++11 allocator model. Prior to C++11 allocators did not appropriately construct other allocators and they were essentially stateless. In case you need to use code prior to C++11 but want to deal with allocators, you might want to use the containers from BSL : these are allocator aware and do compile with C++03 compilers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM