STL容器自定义内存分配器

Posted 双锅首上

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL容器自定义内存分配器相关的知识,希望对你有一定的参考价值。

STL 和 STL 扩展类现在接受额外的模板分配器参数。分配器参数的默认值来自C++标准库std::allocator<T>。您也可以提供自己的分配器,以便在应用程序中自定义内存管理。为了提供您自己的模板类作为分配器,模板必须符合特定接口,包括成员函数和类型定义以及其句法和语义要求。

下面是一个简单的分配器的部分代码片段:

    template <class T>
    class my_allocator 
    {
      typedef size_t    size_type;
      typedef ptrdiff_t difference_type;
      typedef T*        pointer;
      typedef const T*  const_pointer;
      typedef T&        reference;
      typedef const T&  const_reference;
      typedef T         value_type;

      template <class U> 
      struct rebind { typedef allocator<U> other; };
  
      // remaining member functions described below
      // ...
    };

rebind成员允许容器为某些任意数据类型构建分配器,分配器类型由模板参数确定。例如,容器可能需要分配 T 以外的类型(比如list节点或hash buckets)。在这种情况下,容器可以获得正确的类型,通常使用typedef:

typedef A::rebind<Node>::other  Node_Allocator;

分配器模板类需满足上述要求。另外还需要实现下列函数:

构造函数

    my_allocator() throw();     
    my_allocator (const allocator&) throw ();     
    template <class U>     
    my_allocator(const my_allocator<U>&);

析构函数

~my_allocator();

分配运算符

    template <class U>     
    my_allocator& operator=(const my_allocator<U>&) throw();

public成员函数

pointer address(reference r) const;

返回一个指针类型的地址。此函数和下面这个函数用于将引用r转换为指针。

const_pointer address(const_reference r) const;

pointer allocate(size_type n,
allocator<U>::const_pointer hint=0);

分配n个值的存储空间。如果可以,使用hint的值优化存储位置。

void deallocate(pointer);

析构一块存储空间(和allocate是成对的)。

size_type max_size();

返回最大的可用存储空间(allocate可用的存储空间)。

void construct(pointer p, const_reference val);

在特定位置(p的位置)构造类(模板参数T)的对象,并在调用T的构造函数时使用val的值。

void destroy(pointer p);

对p指向的值调用析构函数。

下面的示例定义了一个简单的自定义分配器。这可以用来测试编译器或标准C++库对自定义分配器的支持。它检查代码中传递的分配器参数是否实际使用:

#include <rw/tvdlist.h>
#include <rw/cstring.h>
#include <iostream>

template <class T>
class my_allocator
{
public:
  typedef size_t    size_type;
  typedef ptrdiff_t difference_type;
  typedef T*        pointer;
  typedef const T*  const_pointer;
  typedef T&        reference;
  typedef const T&  const_reference;
  typedef T         value_type;

  my_allocator() {}
  my_allocator(const my_allocator&) {}



  pointer   allocate(size_type n, const void * = 0) {
              T* t = (T*) malloc(n * sizeof(T));
              std::cout
              << "  used my_allocator to allocate   at address "
              << t << " (+)" << std::endl;
              return t;
            }
  
  void      deallocate(void* p, size_type) {
              if (p) {
                free(p);
                std::cout
                << "  used my_allocator to deallocate at address "
                << p << " (-)" << 
                std::endl;
              } 
            }

  pointer           address(reference x) const { return &x; }
  const_pointer     address(const_reference x) const { return &x; }
  my_allocator<T>&  operator=(const my_allocator&) { return *this; }
  void              construct(pointer p, const T& val) 
                    { new ((T*) p) T(val); }
  void              destroy(pointer p) { p->~T(); }

  size_type         max_size() const { return size_t(-1); }

  template <class U>
  struct rebind { typedef my_allocator<U> other; };

  template <class U>
  my_allocator(const my_allocator<U>&) {}

  template <class U>
  my_allocator& operator=(const my_allocator<U>&) { return *this; }
};

int main()
{
  const int numItems = 100;
  std::cout << "\\nCreating a RWTValDlist with a default allocator"
            << std::endl;

  RWTValDlist<RWCString> regular;


  std::cout << "\\nInserting " << numItems
            << " items" << std::endl;

  for (int i = 0; i < numItems; ++i) {
    regular.insert(RWCString('a' + i, i));
  }


  std::cout << "\\n\\nCreating a RWTValDlist with my_allocator type"
            << std::endl;

  RWTValDlist<RWCString, my_allocator<RWCString> > custom;

  std::cout << "\\nInserting " << numItems
            << " items\\n" << std::endl;

  for (int i = 0; i < numItems; ++i) {
    custom.insert(RWCString('a' + i, i));
  }
  
  return 0;
}

程序输出:

Creating a RWTValDlist with a default allocator

Inserting 100 items

Creating a RWTValDlist with my_allocator type
  used my_allocator to allocate   at address 0080ABD0 (+)
  used my_allocator to allocate   at address 0080AC08 (+)

Inserting 100 items

  used my_allocator to allocate   at address 0080AC40 (+)
  used my_allocator to allocate   at address 0080AC78 (+)
  used my_allocator to allocate   at address 0080C6F0 (+)
  used my_allocator to allocate   at address 0080C728 (+)
  used my_allocator to allocate   at address 0080FB28 (+)
  used my_allocator to allocate   at address 00820068 (+)
  used my_allocator to deallocate at address 00820068 (-)
  used my_allocator to deallocate at address 0080FB28 (-)
  used my_allocator to deallocate at address 0080C728 (-)
  used my_allocator to deallocate at address 0080C6F0 (-)
  used my_allocator to deallocate at address 0080AC78 (-)
  used my_allocator to deallocate at address 0080AC40 (-)
  used my_allocator to deallocate at address 0080AC08 (-)
  used my_allocator to deallocate at address 0080ABD0 (-)

可以看到,当没有使用自定义分配器时,正常创建了RWTValDist并插入了100个项。当使用my_allocator实例化列表时,my_allocator的实例被用于堆内存的8次分配和解分配。

以上是关于STL容器自定义内存分配器的主要内容,如果未能解决你的问题,请参考以下文章

STL容器自定义内存分配器

STL 容器的自定义分配器错误

自定义C++ STL内存分配器

带有自定义分配器的 std::string

C++ STL 问题:分配器

STL源代码分析(ch2 内存分配)标准接口