C++ 如何将完全相同的自定义分配器状态传递给多个容器?

C++ 如何将完全相同的自定义分配器状态传递给多个容器?,c++,c++11,allocator,stateful,C++,C++11,Allocator,Stateful,我正在编写一个分配器,其中引用了某个类的另一个实例,该实例跟踪分配的字节数 下面是我尝试做的(改编自)的一个最小示例,只是没有整个内存跟踪类,而是引用了一些int,它收集到目前为止分配的字节。此引用在main内部分配,并应传递给CustomAllocator: #include <limits> // numeric_limits #include <iostream> #include <typeinfo> // typeid // container

我正在编写一个分配器,其中引用了某个类的另一个实例,该实例跟踪分配的字节数

下面是我尝试做的(改编自)的一个最小示例,只是没有整个内存跟踪类,而是引用了一些int,它收集到目前为止分配的字节。此引用在main内部分配,并应传递给CustomAllocator:

#include <limits>   // numeric_limits
#include <iostream>
#include <typeinfo> // typeid

// container
#include <vector>
#include <list>
#include <forward_list>

template<typename T>
class CustomAllocator {
public:
    // type definitions
    typedef T value_type; /** Element type */
    typedef T* pointer; /** Pointer to element */
    typedef T& reference; /** Reference to element */
    typedef const T* const_pointer; /** Pointer to constant element */
    typedef const T& const_reference; /** Reference to constant element */
    typedef std::size_t size_type; /** Quantities of elements */
    typedef std::ptrdiff_t difference_type; /** Difference between two pointers */

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

    // return maximum number of elements that can be allocated
    size_type max_size () const throw() {
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
    }

    CustomAllocator(std::size_t& memAllocated) :
            m_totalMemAllocated(memAllocated) {
        std::cout << "construct " << typeid(T).name() << std::endl;
    }

    CustomAllocator(const CustomAllocator& src) :
            m_totalMemAllocated(src.m_totalMemAllocated) {
        std::cout << "copy construct " << typeid(T).name() << std::endl;
    }

    template<class U>
    CustomAllocator(const CustomAllocator<U>& src) :
            m_totalMemAllocated(src.getTotalMemAllocated()) {
    }

    // allocate but don't initialize num elements of type T
    pointer allocate(size_type num, const void* = 0) {
        m_totalMemAllocated += num * sizeof(T);
        // print message and allocate memory with global new
        std::cout << "allocate " << num << " element(s)" << " of size "
                << sizeof(T) << std::endl;
        pointer ret = (pointer) (::operator new(num * sizeof(T)));
        std::cout << " allocated at: " << (void*) ret << std::endl;
        return ret;
    }

    // deallocate storage p of deleted elements
    void deallocate(pointer p, size_type num) {
        m_totalMemAllocated -= num * sizeof(T);
        // print message and deallocate memory with global delete
        std::cout << "deallocate " << num << " element(s)" << " of size "
                << sizeof(T) << " at: " << (void*) p << std::endl;
        ::operator delete((void*) p);
    }




    // initialize elements of allocated storage p with value value
    // no need to call rebind with this variadic template anymore in C++11
    template<typename _U, typename ... _Args>
    void construct(_U* p, _Args&&... args) {
        ::new ((void *) p) _U(std::forward<_Args>(args)...);
    }

    // destroy elements of initialized storage p
    template<typename _U>
    void destroy(_U* p) {
        p->~_U();
    }

    // return address of values
    pointer address (reference value) const {
        return &value;
    }
    const_pointer address (const_reference value) const {
        return &value;
    }

private:
    std::size_t& m_totalMemAllocated;
};

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

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

int main() {

    std::size_t memAllocated = 0;

    CustomAllocator<int> allocatorInstance(memAllocated);

    std::vector<int> foo(allocatorInstance);
    foo.push_back(23);
    foo.push_back(12);
    foo.push_back(8);

    std::cout << "---" << std::endl;

    // here the same
    std::list<double> bar(allocatorInstance);
    bar.push_back(3.44);
    bar.push_back(1.18);
    bar.push_back(2.25);

    std::cout << "---" << std::endl;

    // debug output
    for (auto x : foo)
        std::cout << x << " ";
    for (auto x : bar)
        std::cout << x << " ";

    std::cout << "\nalloc_count: " << memAllocated << std::endl;

    std::cout << '\n';
    return 0;
}
从main进入全局范围意味着所有使用CustomAllocator的容器最终都将使用全局定义的memAllocated。但我想扩展它,这样我就可以有一些额外的内存或实例memAllocated2,然后再分配给其他一些分配器实例

旁注:

有关不同容器(STD容器除外)的有状态分配器版本,请参阅


为了确保状态在所有分配器实例之间共享,第一个想法是使其成为静态成员。但这还不够,因为不同的模板实例实际上是不同的类型,每个实例都有自己的静态成员副本。因此,我只能想象两种方法:将状态设置为只包含静态成员的辅助类,或者使用单例模式:

辅助类的静态成员:

struct CustomAllocatorState {
    static std::size_t& m_totalMemAllocated;
}
std::size_t& CustomAllocatorState::m_totalMemAllocated = 0; # do not forget definition...

template<typename T>
class CustomAllocator {
public:
    ...
    pointer allocate(size_type num, const void* = 0) {
        CustomAllocatorState::m_totalMemAllocated += num * sizeof(T);
        ...
struct CustomAllocatorState{
静态标准::分配的总尺寸;
}
std::size_t&CustomAllocatorState::m_totalMemAllocated=0;#别忘了定义。。。
模板
类自定义分配器{
公众:
...
指针分配(大小\类型num,常量void*=0){
CustomAllocatorState::m_totalMemAllocated+=num*sizeof(T);
...
< P>单模式(可以使用任何其他C++单模式,这一模式非常简单,但不抵抗静态初始化失败):

类CustomAllocatorState{
CustomAllocatorState():m_val(0){}
静态CustomAllocatorState状态;
公众:
国际货币基金组织;
静态CustomAllocatorState&getState(){
返回状态;
}
};
CustomAllocatorState CustomAllocatorState::state;
模板
类自定义分配器{
公众:
...
CustomAllocator():
状态(CustomAllocatorState::getState()){

一个简单的方法是使每个分配器实例都是“无状态的”并且让类型本身在实例之间保持全局状态。呃…您可能不是指
vector
,而是
vector
?使用此分配器模型,分配器实例必须是无状态的。但是,在C++17中,我们可以使用
std::pmr
命名空间中支持的容器版本。您应该通过分配器的类型作为模板参数,以及构造函数的实例。如
std::vector foo(allocatorInstance);
@IgorTandetnik你是对的!我忘了给容器一个分配器的实例(谢谢:)。我的代码段中似乎有几个错误(比如传递allocatorInstance而不将模板类型传递给contianer)。但是无论如何,当std分配器没有状态时,为什么需要分配器实例呢?
std::size_t memAllocated = 0;
struct CustomAllocatorState {
    static std::size_t& m_totalMemAllocated;
}
std::size_t& CustomAllocatorState::m_totalMemAllocated = 0; # do not forget definition...

template<typename T>
class CustomAllocator {
public:
    ...
    pointer allocate(size_type num, const void* = 0) {
        CustomAllocatorState::m_totalMemAllocated += num * sizeof(T);
        ...
class CustomAllocatorState {
    CustomAllocatorState(): m_val(0) {}
    static CustomAllocatorState state;

public:
    int m_val;
    static CustomAllocatorState& getState() {
        return state;
    }
};
CustomAllocatorState CustomAllocatorState::state;

template<typename T>
class CustomAllocator {
public:
    ...
    CustomAllocator() :
        state(CustomAllocatorState::getState()) {
            std::cout << "construct " << typeid(T).name() << std::endl;
    }
    ...
    pointer allocate(size_type num, const void* = 0) {
        state.m_totalMemAllocated += num * sizeof(T);
        ...
private:
    CustomAllocatorState& state;
};