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

Posted

技术标签:

【中文标题】如何将自定义分配器的完全相同的状态传递给多个容器?【英文标题】:How can I pass the exact same state of an custom allocator to multiple containers? 【发布时间】:2017-08-31 05:32:28 【问题描述】:

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

下面是我正在尝试做的一个最小示例(改编自 here),只是没有整个内存跟踪类,而是引用了一些 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;

我的问题是我不知道如何将分配器实例的完全相同的状态(在示例中为 m_totalMemAllocated)传递给其他两个容器(此处为:foo 和 bar)。由于标准规定 C++11 分配器可以有状态。

更新:

谢谢你到目前为止的答案:)

我知道您通常将 CustomAllocators 作为模板参数传递给 std 容器;像这样:

std::vector<int, CustomAllocator<int> > foo;
std::list<double, CustomAllocator<double> > bar;

另见: Difference between allocator supplied as template parameter and allocator supplied as constructor argument in C++ containers?

但是在这里我确实有一个我无法传递的状态,并且默认构造函数会被调用,除非我给引用一些默认值,否则我无法使用它(但这也不是我想要的)。

放置

std::size_t memAllocated = 0;

从 main 到全局范围意味着所有使用 CustomAllocator 的容器最终都将使用全局定义的 memAllocated。但我想扩展它,这样我就可以拥有一些额外的内存或实例 memAllocated2,然后再次分配给其他一些分配器实例。

旁注:

对于不同于 STD-Containers 的不同容器的分配器的有状态版本,请参阅

How to track memory usage using EASTL?

【问题讨论】:

一种简单的方法是让每个分配器实例都“无状态”,并让类型本身在实例之间保持全局状态。 Err... 大概你的意思不是vector&lt;int&gt;,而是vector&lt;int, CustomAllocator&lt;int&gt;&gt; 使用这种分配器模型,分配器实例需要是无状态的。但是,在 C++17 中,我们可以使用 std::pmr 命名空间中支持 polymorphic memory resources 的容器版本。 您应该将分配器的类型both作为模板参数传递,将实例传递给构造函数。如std::vector&lt;int, CustomAllocator&lt;int&gt; &gt; foo(allocatorInstance); @IgorTandetnik 你是对的!我忘了给容器分配器的实例谢谢:)。我的代码片段中似乎有几个错误(比如传递 allocatorInstance 而没有将模板类型传递给 contianer)。但无论如何。为什么 std 分配器没有状态时需要分配器实例? 【参考方案1】:

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

辅助类的静态成员:

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);
        ...

单例模式(你可以使用任何其他 C++ 单例模式,这个非常简单,但不抵抗静态初始化的失败):

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;
;

辅助类中的静态成员可能更简单,但如果您已经在应用程序中使用了单例模式,那么在这里也可以使用它。

【讨论】:

以上是关于如何将自定义分配器的完全相同的状态传递给多个容器?的主要内容,如果未能解决你的问题,请参考以下文章

尝试将自定义 C++ 矩阵传递给 numpy 数组

如何将自定义数据从ui-router中的视图传递到某个状态?

如何将自定义类型数组传递给 Postgres 函数

如何将自定义类分配给 TableViewController

如何将自定义道具和历史传递给 Route

如何将自定义道具传递给 QueryRenderer 渲染函数?