亲手实践Allocators

《Hands-On System Programming with C++》读书笔记之九

new()无法对C++ STL的对象的内存操作进行管理(如std::list和std::map)。为此,C++又提出了新的概念叫做allocator,allocator类可以对指定的类型定义内存如何分配和释放。

C++ allocator介绍

allocator是C++中的一个类模板,它用于对指定的数据类型进行内存分配管理。有两种不同的allocator:

  • equal allocator
  • unequal allocator
    当可以用一个allocator分配内存,而用另一个来释放这片内存时,称它们为equal allocator,此时 == 操作符返回值为真。例如
myallocator<myclass> myalloc1;
myallocator<myclass> myalloc2;
auto ptr = myalloc1.allocate(1);
myalloc2.deallocate(ptr, 1);

上面的两个allocator是equal allocator。否则称为unequal allocator,unequal allocator通常保存自己的状态(stateful),防止其它状态不同的allocator来释放内存。

基本allocator

最基本的allocator是无状态、等效的allocator(stateless,equal allocator)。下面就是这样一个自定义的例子。

template<typename T>
class myallocator
{
   
public:
    using value_type = T;
    using pointer = T*;
    using size_type = std::size_t;
public:
    myallocator() = default;

    template<typename U>
    myallocator(const myallocator<U> &other) noexcept
    {
   
        (void)other;
    }

    pointer allocate(size_type n)
    {
   
        if (auto ptr = static_cast<pointer>(malloc(sizeof(T) * n)))
        {
   
            return ptr;
        }
        throw std::bad_alloc();
    }

    void deallocate(pointer p, size_type n)
    {
   
        (void)n;
        return free(p);
    }
};

template<typename T1, typename T2>
bool operator == (const myallocator<T1>&, const myallocator<T2>&)
{
   
    return true;
}

template<typename T1, typename T2>
bool operator != (const myallocator<T1>&, const myallocator<T2>&)
{
   
    return false;
}

上面同样是一个模板类,至少要提供一个模板数据类型,这个类型是用于分配内存的对象。同时,allocator必须有一个默认的构造函数,因为C++的容器类会调用指定给它的allocator,这就要求allocator能够在不需要任何参数的条件下执行构造函数。
allocate()函数的输入输格式出是固定的,否则它就无法被已有的容器类正常调用。分配的内存通过static_cast(编译时类型检查)转换为指定数据类型的指针。这里用的是malloc()而不是new(),因为容器类会执行构造工作,allocator只需分配内存,不负责对象的构造。
deallocate()同样原因使用了free()。注意由于我们在创造一个equal allocator,所以这里的ptr不必须是原来的直接分配内存的得到指针。而参数n必须要与原来分配时一致。
类定义中出现了copy constructor。当我们为容器指定allocator时,我们是这样使用的std::list<myclass, myallocator> mylist。std::list类在为自己的节点创建对象时会拷贝myclass{}对象,为了在这个过程中避免shadow copy(只拷贝了指针而没有拷贝内存),需要调用myclass{}的copy constructor。所以在allocator类中必须使用copy constructor。 例子中的*(void)other只是指出other参数不使用,编译器不必警告。
关于 ==
!=* 操作符,由于定义的时equal allocator,allocator()和deallocate()背后的操作只是malloc()和free(),满足“当可以用一个allocator分配内存,而用另一个来释放这片内存”这个条件,因此 *==操作符返回真,!=*返回假。

理解allocator的属性和可选项

属性
返回指针

记住allocator的返回值是指针,可以使用 * 和 -> 对返回的指针进行操作。

等效性

如果对同样数据类型的allocator,==返回真,那么可以用任意一个来自由地释放对象。大部分条件下stateful allocator不是等效的,stateless allocator是等效的,但也不总是这样。
在C++17以前,容器很难判断allocator是否具有等效性,除非真的例化两个allocator的对象再比较它们。因此总是默认假设allocator没有等效性,由此失去了很多优化的空间。在C++17中,通过定义
using is_always_equal = std::true_type
,容器可以判断allocator具有等效性。

不同的内存分配类型

不同的容器需要不同的内存分配,因此allocator需要支持多种的内存分配类型,包括

  • 每次分配的内存要是一片连续空间
  • 一次可以分配多个对象的内存空间
#include <iostream>
#include <list>

template<typename T>
class myallocator
{
   
public:
    using value_type = T;
    using pointer = T*;
    using size_type = std::size_t;
    using is_always_equal = std::true_type;
public:
    myallocator()
    {
   
        std::cout <<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值