常量容器(map) - 消除堆分配

时间:2015-06-10 14:35:55

标签: c++11 dictionary heap

如果我创建一个静态const std :: map,它将在堆上分配内存。以下代码抛出bad_alloc:

#include <iostream>
#include <map>

class A {
public:
    static const std::map<int, int> a;
};

const std::map<int, int> A::a = { { 1, 3} , { 2, 5} };

void* operator new  ( std::size_t count )
{
    throw std::bad_alloc();
}

int
main (void)
{
    for(auto &ai: A::a) {
        std::cout << ai.first << " " << ai.second << "\n";
    }
    return 0;
}

是否可以在不进行内存分配的情况下以某种方式创建此常量映射?

1 个答案:

答案 0 :(得分:0)

正如Igor Tandetnik建议的那样,自定义分配器可以解决问题。以下是一个简单线性分配器的快速简单示例,它从静态缓冲区返回内存插槽:

#include <iostream>
#include <map>
#include <cassert>

template <typename T>
class LinearAllocator {
    static constexpr size_t _maxAlloc = 1<<20;
    using Buffer = std::array<T, _maxAlloc>;
    using FreeList = std::array<bool, _maxAlloc>;

    static Buffer _buffer;
    static FreeList _allocated;
public:
    typedef T* pointer;
    typedef T value_type;

    template<typename U>
    struct rebind { typedef LinearAllocator<U> other; };

    pointer allocate(size_t /*n*/, const void *hint=0) {
        for(size_t i = 0; i < _maxAlloc; ++i) {
            if(!_allocated[i]) {
                _allocated[i] = true;
                return &_buffer[i];
            }
        }
        throw std::bad_alloc();
    }

    void deallocate(pointer p, size_t /*n*/) {
        assert(p >= &_buffer[0] && p < &_buffer[_maxAlloc]);
        _allocated[p-&_buffer[0]] = false;
    }

    LinearAllocator() throw() { }
    LinearAllocator(const LinearAllocator &a) throw() { }
    template <class U>                    
    LinearAllocator(const LinearAllocator<U> &a) throw() { }
    ~LinearAllocator() throw() { }
};

template <typename T>
typename LinearAllocator<T>::Buffer LinearAllocator<T>::_buffer;

template <typename T>
typename LinearAllocator<T>::FreeList LinearAllocator<T>::_allocated;

using MyMap = std::map<int, int, std::less<int>,
                       LinearAllocator<std::pair<int,int> > >;

// make sure we notice if new gets called
void* operator new(size_t size) {
    std::cout << "new called" << std::endl; 
}

int main() {
    MyMap m;
    m[0] = 1; m[1] = 3; m[2] = 8;

    for(auto & p : m)
        std::cout << p.first << ": " << p.second << std::endl;

    return 0;
}

输出:

0: 1
1: 3
2: 8

请注意,此分配器一次只能处理单个插槽的请求。我确定你会根据你的要求弄清楚如何扩展它。