Post

STL: Allocator

分配器, 通常与容器共同使用而不单独使用, 作用是给容器指定一种分配空间的形式.

你硬要用的话毕竟它是个类, 可以直接调用. 但个人认为你不该这样用, 这就好像装机时候的防呆口一样, 防呆不防傻, 人家设计的时候就防止你单独使用从而阻碍你使用了, 你还硬要用.

1
2
3
4
5
6
7
8
int *p;
allocator<int> alloc1;
int size;
cin >> size;
p = alloc1.allocate(size);
// 和内存分配不同, 如果你要用分配器, 你必须手动输入要释放的元素个数
alloc1.deallocate(size);
// 其他一些分配器

allocatedeallocate

源码很长, 因此我们逐步分析. 对于分配器来说, 最重要的两个成员函数就是上述的用于分配内存和回收内存.

1
2
3
4
5
6
7
8
9
10
11
12
13
// allocate
_NODISCARD _DECLSPEC_ALLOCATOR _Ty *allocate(_CRT_GUARDOVERFLOW const size_t _Count)
{ // allocate array of _Count elements
    // 看到没, 这里一个静态的指针类型转换, 将万能指针转换为容器中元素相同类型的指针, 因此_Allocate() 就是开辟内存的步骤
    return (static_cast<_Ty *>(_Allocate<_New_alignof<_Ty>>(_Get_size_of_n<sizeof(_Ty)>(_Count))));
}

_NODISCARD _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _DECLSPEC_ALLOCATOR _Ty *allocate(
    _CRT_GUARDOVERFLOW const size_t _Count, const void *)
{ // allocate array of _Count elements, ignore hint
    // 这个就不管, 重载多了一个额外的指针参数, 用于了解你最终需要分配的是 _Count 个什么类型的东西, 最终调用的都是上面那个重载
    return (allocate(_Count));
}

前面很长一串的返回类型可以先不管, 总之能看出返回的应该是一个指针, 这与我们的期望也是相同的. 因为分配内存的具体过程是, 开辟内存->指针转换, 这里返回的指针势必能被容器接受从而接受该指针对应的内存块.

深入下去看一下, 一个仿函数调用了全局 new, 完事儿, 就是和理论上一样的.

你要用 vscode 代码跳转功能去看的话, 它会跳转至和函数形参匹配的对应函数, 你需要找到最初的仿函数或函数模板才能知道它真正在干什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct _Default_allocate_traits
{
    _DECLSPEC_ALLOCATOR static void *_Allocate(const size_t _Bytes)
    {
        return (::operator new(_Bytes));
    }

#if _HAS_ALIGNED_NEW
    _DECLSPEC_ALLOCATOR static void *_Allocate_aligned(const size_t _Bytes, const size_t _Align)
    {
        return (::operator new (_Bytes, align_val_t{_Align}));
    }
#endif /* _HAS_ALIGNED_NEW */
};

对于 deallocate, 我们可以同样分析, 实际调用全局 delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void deallocate(_Ty *const _Ptr, const size_t _Count)
{ // deallocate object at _Ptr
    // no overflow check on the following multiply; we assume _Allocate did that check
    _Deallocate<_New_alignof<_Ty>>(_Ptr, sizeof(_Ty) * _Count);
}

template <size_t _Align,
          enable_if_t<(!_HAS_ALIGNED_NEW || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__), int> = 0>
inline void _Deallocate(void *_Ptr, size_t _Bytes)
{ // deallocate storage allocated by _Allocate when !_HAS_ALIGNED_NEW || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__
#if defined(_M_IX86) || defined(_M_X64)
    if (_Bytes >= _Big_allocation_threshold)
    { // boost the alignment of big allocations to help autovectorization
        _Adjust_manually_vector_aligned(_Ptr, _Bytes);
    }
#endif /* defined(_M_IX86) || defined(_M_X64) */

    ::operator delete(_Ptr, _Bytes);
}

源码

MSVC 14.16.27023 中 <xmemory>, 实际上能够看成 newdelete 的一层封装. 在实际应用中, 还存在更多中的分配器, 以实现诸如内存池等其他的分配模式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
template <class _Ty>
class allocator
{ // generic allocator for objects of class _Ty
public:
    static_assert(!is_const_v<_Ty>,
                  "The C++ Standard forbids containers of const elements "
                  "because allocator<const T> is ill-formed.");

    using _Not_user_specialized = void;

    using value_type = _Ty;

    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef _Ty *pointer;
    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef const _Ty *const_pointer;

    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef _Ty &reference;
    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef const _Ty &const_reference;

    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef size_t size_type;
    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS typedef ptrdiff_t difference_type;

    using propagate_on_container_move_assignment = true_type;
    using is_always_equal = true_type;

    template <class _Other>
    struct _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS rebind
    { // convert this type to allocator<_Other>
        using other = allocator<_Other>;
    };

    _NODISCARD _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _Ty *address(_Ty &_Val) const noexcept
    { // return address of mutable _Val
        return (_STD addressof(_Val));
    }

    _NODISCARD _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS const _Ty *address(const _Ty &_Val) const noexcept
    { // return address of nonmutable _Val
        return (_STD addressof(_Val));
    }

    constexpr allocator() noexcept
    { // construct default allocator (do nothing)
    }

    constexpr allocator(const allocator &) noexcept = default;
    template <class _Other>
    constexpr allocator(const allocator<_Other> &) noexcept
    { // construct from a related allocator (do nothing)
    }

    void deallocate(_Ty *const _Ptr, const size_t _Count)
    { // deallocate object at _Ptr
        // no overflow check on the following multiply; we assume _Allocate did that check
        _Deallocate<_New_alignof<_Ty>>(_Ptr, sizeof(_Ty) * _Count);
    }

    _NODISCARD _DECLSPEC_ALLOCATOR _Ty *allocate(_CRT_GUARDOVERFLOW const size_t _Count)
    { // allocate array of _Count elements
        return (static_cast<_Ty *>(_Allocate<_New_alignof<_Ty>>(_Get_size_of_n<sizeof(_Ty)>(_Count))));
    }

    _NODISCARD _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS _DECLSPEC_ALLOCATOR _Ty *allocate(
        _CRT_GUARDOVERFLOW const size_t _Count, const void *)
    { // allocate array of _Count elements, ignore hint
        return (allocate(_Count));
    }

    template <class _Objty,
              class... _Types>
    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS void construct(_Objty *const _Ptr, _Types &&..._Args)
    { // construct _Objty(_Types...) at _Ptr
        ::new (const_cast<void *>(static_cast<const volatile void *>(_Ptr)))
            _Objty(_STD forward<_Types>(_Args)...);
    }

    template <class _Uty>
    _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS void destroy(_Uty *const _Ptr)
    { // destroy object at _Ptr
        _Ptr->~_Uty();
    }

    _NODISCARD _CXX17_DEPRECATE_OLD_ALLOCATOR_MEMBERS size_t max_size() const noexcept
    { // estimate maximum array size
        return (static_cast<size_t>(-1) / sizeof(_Ty));
    }
};
This post is licensed under CC BY 4.0 by the author.