iOS中dealloc实现机制 deallocate cursor

c++ 疑难杂症() allocator

在实践中曾经有个需求: 系统会陆续生产字节固定大小串, 检查是否重复,不重复就添加, 最大的数据量可达到万条记录,不使用磁盘,在内存中处理。

尝试了纯内存的sqlite, 占用内存太大了, 放弃;

也尝试直接使用std::map/std::set,都因为内存占用太大了,放弃;

最后是从std::map里把红黑树给抠出来, 使用整型及位域来减少节点变量大小, 一次性分配多个节点的内存之类的, 从而实现内存不超过230M, 满足了业务需求。

当时,如果是对std::allocator有所了解, 也许不用这么折腾了; 现在来学习学习。

1. 标准文档定义

std::allocator - cppreference.com


在标头 定义


template< class T > struct allocator;


template<> struct allocator;

(C++ 中弃用) (C++ 中移除)



如果不提供用户指定的分配器,那么 std::allocator 类模板是所有标准库容器使用的默认分配器。 默认分配器无状态,即给定分配器的任何实例都可交换、比较相等,且能由同一分配器类型的任何其他实例释放分配的内存。

对 void 的显式特化缺少成员类型定义(typedef)referenceconst_referencesize_typedifference_type 此特化不声明成员函数。

(C++ 前)

默认分配器满足分配器完整性要求。

(C++ 起)

成员类型

类型

定义

value_type

T

pointer

(C++ 中弃用)

(C++ 中移除)

T*

const_pointer

(C++ 中弃用)

(C++ 中移除)

const T*

reference

(C++ 中弃用)

(C++ 中移除)

T&

const_reference

(C++ 中弃用)

(C++ 中移除)

const T&

size_type

std::size_t

difference_type

std::ptrdiff_t

propagate_on

_container

_move_assignment

(C++)

std::true_type

rebind

(C++ 中弃用)

(C++ 中移除)

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

is_always_equal

(C++)

(C++ 中弃用)

(C++ 中移除)

std::true_type

成员函数

(构造函数)

创建新的分配器实例 (公开成员函数)

(析构函数)

析构分配器实例 (公开成员函数)

address(C++ 前)

获得对象的地址,即使重载了 operator& (公开成员函数)

allocate

分配未初始化的存储 (公开成员函数)

allocate_at_least(C++)

分配与请求的大小至少一样大的未初始化存储 (公开成员函数)

deallocate

解分配存储 (公开成员函数)

max_size(C++ 前)

返回最大的受支持分配大小 (公开成员函数)

construct(C++ 前)

在分配的存储中构造对象 (公开成员函数)

destroy(C++ 前)

析构已分配存储中的对象 (公开成员函数)

非成员函数

operator==

operator!=(C++ 中移除)

比较两个分配器实例 (公开成员函数)

注解

成员模板 rebind 提供获得不同类型的分配器的方式。例如,std::list 在分配某个内部类型 Node 节点时会使用分配器A::rebind>::other (C++ 前)std::allocator_traits::rebind_alloc>,它在 Astd::allocator 时以 A::rebind>::other (C++ 起) 实现。

成员类型 is_always_equal 由 LWG 问题 弃用,因为它使得派生自 std::allocator 的定制分配器默认被当作始终相等。
std::allocator_traitsstd::allocator::is_always_equal 未被弃用,而它的成员常量
value 对任何 T 均为 true。

示例

#include 
#include 
#include 
 
int main()
{
    // int 的默认分配器
    std::allocator alloc1;
 
    // 演示少见的直接使用成员
    static_assert(std::is_same_v);
    int* p1 = alloc1.allocate(1);  // 一个 int 的空间
    alloc1.deallocate(p1, 1);      // 而它没了
 
    // 这些都可以通过特征使用,所以不需要直接使用
    using traits_t1 = std::allocator_traits; // 匹配的特征
    p1 = traits_t1::allocate(alloc1, 1);
    traits_t1::construct(alloc1, p1, 7);  // 构造 int
    std::cout << *p1 << '\n';
    traits_t1::deallocate(alloc1, p1, 1); // 解分配 int 的空间
 
    // string 的默认分配器
    std::allocator alloc2;
    // 匹配的特征
    using traits_t2 = std::allocator_traits;
 
    // 用 string 的特征重绑定产生同一类型
    traits_t2::rebind_alloc alloc_ = alloc2;
 
    std::string* p2 = traits_t2::allocate(alloc2, 2); // 2 个 string 的空间
 
    traits_t2::construct(alloc2, p2, "foo");
    traits_t2::construct(alloc2, p2 + 1, "bar");
 
    std::cout << p2[0] << ' ' << p2[1] << '\n';
 
    traits_t2::destroy(alloc2, p2 + 1);
    traits_t2::destroy(alloc2, p2);
    traits_t2::deallocate(alloc2, p2, 2);
}

输出:

7
foo bar

构造函数



allocator() throw();

(C++ 前)

allocator() noexcept;

(C++ 起) (C++ 前)

constexpr allocator() noexcept;

(C++ 起)



allocator( const allocator& other ) throw();

(C++ 前)

allocator( const allocator& other ) noexcept;

(C++ 起) (C++ 前)

constexpr allocator( const allocator& other ) noexcept;

(C++ 起)



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

(C++ 前)

template< class U > allocator( const allocator& other ) noexcept;

(C++ 起) (C++ 前)

template< class U > constexpr allocator( const allocator& other ) noexcept;

(C++ 起)



构造默认分配器。因为默认分配器是无状态的,故构造函数无可见效应。

参数

other

用以构造的另一 allocator

2. 探索

自定义allocator 要求(c++起, c++前)

allocator() noexcept;

allocator( const allocator& other ) noexcept;

template< class U > allocator( const allocator& other ) noexcept;

value_type T

size_type std::size_t

difference_type std::ptrdiff_t

allocate

deallocate

address(非必要)

max_size(非必要)

construct(非必要)

destroy(非必要)


自定义allocator 示例

#include 
#include 

template 
class Allocator1 {
public:
    //成员类型
    using value_type = _Ty;
    //using size_type = size_t;
    //using difference_type = ptrdiff_t;

    //构造函数
    constexpr Allocator1() noexcept {}
    constexpr Allocator1(const Allocator1&) noexcept = default;
    template 
    constexpr Allocator1(const Allocator1<_Other>&) noexcept {}

    //成员函数
    _Ty* allocate(const size_t _Count) {
        return alloc.allocate(_Count);
    }

    void deallocate(_Ty* const _Ptr, const size_t _Count) {
        return alloc.deallocate(_Ptr, _Count);
    }
#if 0
//非必要实现
    template 
    void construct(_Objty* const _Ptr, _Types&&... _Args) {
        return alloc.construct<_Objty, _Types ...>(_Ptr, std::forward<_Types>(_Args) ... );
    }

    template 
    void destroy(_Uty* const _Ptr) {
        alloc.destroy<_Uty>(_Ptr);
    }

    size_t max_size() const noexcept {
        return alloc.max_size();
    }

    template 
    struct rebind {
        using other = Allocator1<_Other>;
    };
    _Ty* address(_Ty& _Val) const noexcept {
        return alloc.address(_Val);
    }

    const _Ty* address(const _Ty& _Val) const noexcept {
        return alloc.address(_Val);
    }
#endif
private:
    std::allocator<_Ty> alloc;
};

int main() {

    class A {
        int val = 0;
    public:
        A(int x) : val(x){
            std::cout << "A(" << val << ")" << std::endl;
        }
        ~A() {
            std::cout << "~A(" << val << ")" << std::endl;
        }
    };

    std::vector> vec;
    for (int i = 0; i < ; i++) {
        vec.emplace_back(i);
    }
    auto alloc = vec.get_allocator();
    auto x = alloc.allocate(1);
    //alloc.construct(x, );
    new (x) A);
    x->~A();
    alloc.deallocate(x, 1);
    return 0;
}
#include 
#include 
#include 

template 
class Allocator2 : public std::allocator<_Ty> {
//class Allocator2 : private std::allocator<_Ty> {
public:      
    //使用父类构造函数
    using std::allocator<_Ty>::allocator;
    using std::allocator<_Ty>::value_type;

    //成员函数
    _Ty* allocate(const size_t _Count) {
        return std::allocator<_Ty>::allocate(_Count);
    }

    void deallocate(_Ty* const _Ptr, const size_t _Count) {
        return std::allocator<_Ty>::deallocate(_Ptr, _Count);
    }
};

int main() {

    class A {
        int val = 0;
    public:
        A(int x) : val(x) {
            std::cout << "A(" << val << ")" << std::endl;
        }
        ~A() {
            std::cout << "A(" << val << ")" << std::endl;
        }
    };

    std::vector> vec;
    for (int i = 0; i < ; i++) {
        vec.emplace_back(i);
    }
    //vec.get_allocator(); linux出错

    std::map, Allocator2>> map;
    map.insert({1, 1});
    //map.get_allocator(); linux出错
    
    return 0;
}
#include 
#include 
#include 

template 
class Allocator3 {
public:
    //成员类型
    using value_type = _Ty;

    //构造函数
    constexpr Allocator3() noexcept {}
    constexpr Allocator3(const Allocator3& x) noexcept = default;
    template 
    constexpr Allocator3(const Allocator3<_Other>& x) noexcept {
        totalSize = x.totalSize;
    }

    //成员函数
    _Ty* allocate(const size_t _Count) {
        size_t size = _Count * sizeof(_Ty);
        totalSize += size;
        return (_Ty*)std::malloc(size);
    }

    void deallocate(_Ty* const _Ptr, const size_t _Count) {
        std::free(_Ptr);
    }

    void Show() {
        std::cout << "分配大小: " << totalSize << std::endl;
    }

    size_t totalSize = 0;

};

int main() {

    class A {
        int val = 0;
    public:
        A(int x) : val(x) {
            std::cout << "A(" << val << ")" << std::endl;
        }
        ~A() {
            std::cout << "A(" << val << ")" << std::endl;
        }
    };

    std::vector> vec;
    for (int i = 0; i < ; i++) {
        vec.emplace_back(i);
    }
    vec.get_allocator().Show();

    std::map, Allocator3>> map;
    map.insert({ 1, 1 });
    map.get_allocator().Show();

    return 0;
}

3. 总结

通过上面的学习, 对allocator有了一定的了解, 以后碰上了, 不至于一头雾水。

有兴趣可以测试下 std::unordered_map 与 std::map 空间占比。

c++ 疑难杂症(3) 模板特化

c++ 疑难杂症(2) std::move

c++ 疑难杂症(6) std::map

c++ 疑难杂症(5) std::pair

c++ 疑难杂症(7) std::tuple

c++ 疑难杂症(1) std::thread

c++ 疑难杂症(9) std::array

c++ 疑难杂症(4) std:vector

c++ 疑难杂症(8) std::multimap

c++ 疑难杂症() std::forward_list

c++ 疑难杂症() std::initializer_list

c++ 疑难杂症() unordered_map

原文链接:,转发请注明来源!