跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

SGI STL 空间配置器原理及 uninitialized 系列函数解析

综述由AI生成详细解析了 SGI STL 空间配置器的实现原理及其与标准 Allocator 的区别。SGI 配置器采用两级结构:一级配置器直接使用 malloc/free 处理大内存;二级配置器针对小内存(小于 128 字节)使用内存池和自由链表(free-list)管理,以减少内存碎片。此外,文章还介绍了 construct 和 destroy 函数如何通过类型特性判断优化对象析构过程,以及 uninitialized_copy、uninitialized_fill 等函数如何区分 POD 与非 POD 类型,利用 memmove 或循环构造来高效初始化未初始化内存。

念念不忘发布于 2026/3/25更新于 2026/5/2726 浏览
SGI STL 空间配置器原理及 uninitialized 系列函数解析

空间配置器 (Allocator 的标准接口)

template<class _Tp>
class allocator {
    typedef alloc _Alloc; // The underlying allocator.
public:
    // type 的设计
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef _Tp *pointer;
    typedef const _Tp *const_pointer;
    typedef _Tp &reference;
    typedef const _Tp &const_reference;
    typedef _Tp value_type;

    // rebind 可以允许 allocator<T> 转化成另一种类型的对象
    // 例如从 allocator<int> 转化到 allocator<double>
    template<class _Tp1>
    struct rebind {
        typedef allocator<_Tp1> other;
    };

    // 默认构造
    allocator() __STL_NOTHROW {}
    // 拷贝构造
    allocator(const allocator &) __STL_NOTHROW {}
    // 泛化拷贝构造
    template<class _Tp1>
    allocator(const allocator<_Tp1>&) __STL_NOTHROW {}
    // 析构
    ~allocator() __STL_NOTHROW {}

    pointer address(reference __x) const { return &__x; }
    const_pointer address(const_reference __x) const { return &__x; }

    // __n is permitted to be 0. The C++ standard says nothing about what
    // the return value is when __n == 0.
    _Tp *allocate(size_type __n, const void* = 0) {
        return __n != 0 ? static_cast<_Tp *>(_Alloc::allocate(__n * sizeof(_Tp))) : 0;
    }

    // __p is not permitted to be a null pointer.
    void deallocate(pointer __p, size_type __n) {
        _Alloc::deallocate(__p, __n * sizeof(_Tp));
    }

    size_type max_size() const __STL_NOTHROW {
        return size_t(-1) / sizeof(_Tp);
    }

    // 这里比较耐人寻味,此处的 new 是 placement new,与我们常规使用的 new 不同的是
    // 常规 new 的时候会执行两步操作:
    // 1: 调用 ::operator new 申请一块原始内存
    // 2: 执行对象的构造函数 -> new(T::T())
    // 这里的第一个参数是一个指向开辟好原始内存空间的指针,第二个参数才是对象要构造的值
    void construct(pointer __p, const _Tp &__val) {
        // 这行代码并不会再开辟新的内存空间,而是将通过_Tp() 构造的对象放入__p 所指向的空间
        new(__p) _Tp(__val);
    }

    // 这里析构对象也是同理,我们常用的 delete 实际上也是两步操作
    // 1: 执行~T() 对象的析构函数
    // 2: 调用 ::operator delete 释放空间
    void destroy(pointer __p) {
        __p->~_Tp();
    }
};

上述是 C++ 标准设计的 Allocator 源代码,通过阅读我们获得一些启发:空间配置器不是单纯的使用 new 和 delete 来分配空间。

空间的分配分为两步:

  1. 先通过 ::operator new 来申请原始内存空间,在这一步的时候就类似于 malloc 并不会构造对象。
  2. 再通过 placement new -> new(__p) T(value) 来构造对象并将对象放入 __p 指向的空间中。

空间的回收在注释中有写则不再重复。如此做的好处是,方便控制 STL 构造和析构对象的时机。

但是,SGI STL 并没有采用这种空间配置的方式,主要原因是在频繁分配小对象的时候效率不佳,且整个类只是对 ::operator new 和 ::operator delete 做了简单的封装。

SGI STL 则使用了一种与众不同的配置器 (alloc),它具有次配置力 (sub-allocation),且它不接受任何参数,它对配置器做了分级结构,分为一级配置器和二级配置器,尤其在分配小空间的时候效率极佳。

SGI 的特殊空间配置器 std::alloc

为了精密分工,在上面提到了,STL allocator 将 内存空间的配置/释放 和 对象内容的构造/析构 拆解开来,空间配置器写在 <memory> 中。 SGI 的 <memory> 则包含了这三个文件 (节选了与空间配置器有关的):

// #include <stl_algobase.h>
#include <stl_alloc.h>       // 负责空间配置和释放
#include <stl_construct.h>   // 负责对象构造和析构
// #include <stl_tempbuf.h>
#include <stl_uninitialized.h> // 用来填充或复制大块内存数据,与对象的初值设置有关
// #include <stl_raw_storage_iter.h>

构造和析构的基本工具 construct() 和 destroy()

为了更好的理解构造和析构过程,节选一段 stl_construct.h 的源代码,观察对象构造和析构的过程。

#include <new.h>
// 想使用 ::operator new 必须要包含这个头文件
// 这个构造为带参构造 (拷贝构造)
// 本质就是在已有的空间上构造对象
template<class _T1, class _T2>
inline void _Construct(_T1 *__p, const _T2 &__value) {
    new ((void*)__p) _T1(__value);
}

// 默认构造 - 常使用在构建多个容器对象
// vector<int> (10);
// 内部就会循环调用
// _Construct(p);
// _Construct(p + 1);
// ...
template<class _T1>
inline void _Construct(_T1 *__p) {
    new ((void*)__p) _T1();
}

// 这个是 destroy 的第一个版本,接收一个指针,析构一个对象
template<class _Tp>
inline void _Destroy(_Tp *__pointer) {
    __pointer->~_Tp();
}

// 这是 destroy 的第二个版本,接收两个迭代器,此函数会先找出元素的数值型别
// 进而利用 __type_traits<> 求取最合适的措施
template<class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
    __destroy(__first, __last, __VALUE_TYPE(__first));
}

// 判断这个元素数值类别有没有 (trivial destructor) 即编译器默认生成的析构函数
template<class _ForwardIterator, class _Tp>
inline void __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp *) {
    typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor;
    __destroy_aux(__first, __last, _Trivial_destructor());
}

// 元素有 trivial destructor
template<class _ForwardIterator>
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}

// 元素有 non-trivial destructor(即程序员手写的析构函数)
template<class _ForwardIterator>
void __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type) {
    for (; __first != __last; ++__first) destroy(&*__first);
}

// 针对常见类型的特化版本
inline void _Destroy(char*, char*) {}
inline void _Destroy(int*, int*) {}
inline void _Destroy(long*, long*) {}
inline void _Destroy(float*, float*) {}
inline void _Destroy(double*, double*) {}

从上述源码中可以观察到,stl 提供了一种通过迭代器大范围销毁对象的方式,但是我们也不知道这个范围有多大。如果这个范围很大 (上百万个对象),如果不分青红皂白一律调用 ~T() 就会大大损失性能,所以这里根据元素类型做了一些判断。

当使用传入两个迭代器析构对象时,函数会先判断元素数值类型,紧接着再去观察这种类型有没有 trivial destructor (编译器生成的析构)。如果有则不执行任何操作,如果是 non-trivial destructor 则按顺序依次执行析构函数。

下面举个例子来观察优化前后的区别:

vector<int> v(1000000);

当上面这个 vector 销毁时,如果不进行类型判断,就会默认调用 100 万次析构 (然而这个析构并无任何作用)。但是当进行优化后,整个过程不需要调用一次析构。

空间的配置和释放 std::alloc

C++ 内存的基本配置操作为 ::operator new,内存释放的基本操作为 ::operator delete 这俩全局函数,就相当于 C 的 malloc() 和 free(),而 SGI 正是通过 malloc() 和 free() 完成的内存配置和释放。

考虑到标准 allocator 无法解决小型区块导致内存破碎的问题,所以 SGI 配置了双层级配置器。一级配置器直接使用 malloc(), free(),而第二级配置器则根据不同情况来选择不同策略。

  • 当配置区块大于 128byte 的时候,会被视为'足够大',会调用第一级配置器。
  • 当配置区块小于 128byte 的时候,被视为'过小',则会使用复杂的 memory pool 方式来管理内存。

而是只使用第一级配置器还是同时开放第二级配置器则通过是否 define __USE_MALLOC 来决定 (但是不管无论如何,都要使用 malloc)。

#ifdef __USE_MALLOC
typedef __malloc_alloc_template<0> malloc_alloc; // 令 alloc 为第一级配置器
typedef malloc_alloc alloc;
#else
// ...
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc; // 令 alloc 为第二级配置器
typedef __default_alloc_template<false, 0> single_client_alloc;
#endif

其中 __malloc_alloc_template 就是第一级配置器,__default_alloc_template 为第二级配置器。但是不管 alloc 被定义为第一或者第二级配置器,SGI 都会给它再包一层接口,使整个配置器能够符合 STL 的规格。

template<class _Tp, class _Alloc>
class simple_alloc {
public:
    static _Tp *allocate(size_t __n) {
        return 0 == __n ? 0 : (_Tp *)_Alloc::allocate(__n * sizeof(_Tp));
    }
    static _Tp *allocate(void) {
        return (_Tp *)_Alloc::allocate(sizeof(_Tp));
    }
    static void deallocate(_Tp *__p, size_t __n) {
        if (0 != __n) _Alloc::deallocate(__p, __n * sizeof(_Tp));
    }
    static void deallocate(_Tp *__p) {
        _Alloc::deallocate(__p, sizeof(_Tp));
    }
};

上述四个内部成员都是简单的转调用,调用传递给配置器 (可以是第一级也可以是第二级),配置单位则从 byte 转化到一个元素的大小。SGI 的所有 STL 容器都使用这个 simple_alloc 接口。

第一级配置器 __malloc_alloc_template 剖析

这里贴出第一级配置器源码,剖析则放在注释中。

template<int __inst>
class __malloc_alloc_template {
private:
    // 处理 oom(out of memory) 内存不足的情况
    static void *_S_oom_malloc(size_t);
    static void *_S_oom_realloc(void*, size_t);
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
    static void (*__malloc_alloc_oom_handler)();
#endif
public:
    static void *allocate(size_t __n) {
        // 第一级配置器,直接使用 malloc
        void *__result = malloc(__n);
        if (0 == __result) // 内存不足调用 oom 函数,这里在下文会细说
            __result = _S_oom_malloc(__n);
        return __result;
    }

    static void deallocate(void *__p, size_t /* __n */) {
        // 直接调用 free
        free(__p);
    }

    static void *reallocate(void *__p, size_t /* old_sz */, size_t __new_sz) {
        // 直接调用 realloc
        void *__result = realloc(__p, __new_sz);
        if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
        return __result;
    }

    // __set_malloc_handler 相当于仿真 C++ 中的 set_new_handler,换句话说,你可以通过它来指定自己的 oom 后的处理策略
    static void (*__set_malloc_handler(void(*__f)()))() {
        void (*__old)() = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = __f;
        return (__old);
    }
};

// malloc_alloc out-of-memory handling
// 处理内存不足,无法配置的情况,需要等待用户自己设置,否则默认为 0
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template<int __inst>
void (*__malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif

// 处理 oom 函数
template<int __inst>
void *__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n) {
    void (*__my_malloc_handler)();
    void *__result;
    // 不断执行释放配置流程
    for (;;) {
        __my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == __my_malloc_handler) {
            __THROW_BAD_ALLOC;
        }
        (*__my_malloc_handler)(); // 调用用户传入的处理函数,企图释放内存
        __result = malloc(__n); // 再次尝试配置内存
        if (__result) return (__result);
    }
}

template<int __inst>
void *__malloc_alloc_template<__inst>::_S_oom_realloc(void *__p, size_t __n) {
    void (*__my_malloc_handler)();
    void *__result;
    for (;;) {
        __my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == __my_malloc_handler) {
            __THROW_BAD_ALLOC;
        }
        (*__my_malloc_handler)(); // 与 oom_malloc 的处理方式相同
        __result = realloc(__p, __n);
        if (__result) return (__result);
    }
}

第一级配置器使用 malloc, realloc, free 等 C 语言函数来配置空间,同时还实现了类似 C++ new-handle 的机制。它无法直接使用 new-handle 机制,因为它没有使用 ::operator new,而是使用了 malloc。

new-handle 机制:当系统无法满足用户的内存配置要求时,调用一个用户所指定的函数,即在调用 ::operator new 时内存空间不足,在抛出 bad_alloc() 之前,会先调用用户传入的处理例程。

第一级配置器在调用 allocate 和 reallocate 失败后就会执行 _S_oom_malloc 和 _S_oom_realloc,希望可以通过用户传入的方法来找到一些空内存,但是当用户没有传入方法的时候,则会直接抛出 bad_alloc 异常。

第二级配置器 __default_alloc_template

第二级配置器多了一些机制,避免太多小额区块造成的内存的碎片 (主要解决的是内碎片问题)。第二层配置器会将大于 128byte 的内存配置交给第一层配置器,而小于 128byte 的则使用 memory pool 来管理。

  • 每次将会配置一大块内存,并维护 free-list。
  • 如果下次有相同的内存配置请求则直接去查找对应的 free-list,从 free-list 中取出一个空间的内存块。
  • 当内存被归还时,也会查找对应大小的 free-list 并挂到上面。
  • 为了方便管理,会将内存申请自动对齐 (RoundUp) 到 8 的倍数,free-list 的个数 = 128 / 8 = 16 个。

下面是第二级内存配置器实现部分源码。

// Important implementation properties:
// 1. If the client request an object of size > _MAX_BYTES, the resulting
// object will be obtained directly from malloc.
// 2. In all other cases, we allocate an object of size exactly
// _S_round_up(requested_size). Thus the client has enough size
// information that we can return the object to the proper free list
// without permanently losing part of the object.
enum { _ALIGN = 8 };             // 对齐方式
enum { _MAX_BYTES = 128 };       // 向第二级配置器申请的最大内存
enum { _NFREELISTS = 16 };       // _MAX_BYTES/_ALIGN

template<bool threads, int inst>
class __default_alloc_template {
private:
    // 将内存对齐到 8 的倍数
    static size_t _S_round_up(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN - 1) & ~((size_t)_ALIGN - 1));
    }

    // 定义自由链表的节点
    // 这里使用 Union(共用体) 是为了一物两用:
    // 1. 可以被视为一个指针,指向下一个 obj 对象
    // 2. 视为一个指针,指向实际区块
    // 不会因为为了维护链表采用指针而造成的内存浪费
    // 这里说一下我的理解:
    // union 计算大小的时候是按照占空间最大的成员来计算的,当多个成员使用 union 的时候
    // 只有一个成员会生效
    // 例如 union A 中有两个成员,这个 union 既可以当 int 用也可以当 char[4] 用
    // union A {
    //     int a;
    //     char b[4];
    // };
    // 如果用 struct 存 obj 节点的话,所消耗的内存就是 8b(指针) + 16b(用户数据) /* struct node{ node* next; // 假设 8byte(64 位) char data[16]; } */
    // 但是如果用 union 存就是 16b(最大成员大小),节省了指针消耗的内存
    // 即用户内存 = 链表节点
    /* 内存被当作指针时 block1 +---------+ | next | -----> block2 +---------+ 此时 _Obj->_M_free_list_link */
    /* 内存被当作用户空间 block1 +----------------+ | user data | +----------------+ 此时 _Obj->_M_client_data */
    // char _M_client_data[1]; 也不是只给一个字节的大小存数据
    // 而是一种占位方式,标记用户数据从 _M_client_data[1] 开始
    // &obj->_M_client_data[0]
    /* 实际内存空间 +----------------+ | union Obj | | client data | <-指向用户数据的起始位置 | ........... | +----------------+ */
    // 至于为什么不从 0 开始而是 1,主要是因为老版本 C++ 不支持 [] 也不允许 [0]
    // 申请一个 16B 的空间情况如图
    /* +--------------------------+
    // | union Obj |
    // | data[0] |
    // | data[1] |
    // | data[2] |
    // | data[3] |
    // | data[4] |
    // | data[5] |
    // | data[6] |
    // | data[7] |
    // | data[8] |
    // | data[9] |
    // | data[10] |
    // | data[11] |
    // | data[12] |
    // | data[13] |
    // | data[14] |
    // | data[15] |
    // +--------------------------+ */
    __PRIVATE:
    union _Obj {
        union _Obj *_M_free_list_link; // 指向下一个内存块
        char _M_client_data[1];        /* The client sees this. */
    };

private:
    // 自由链表本体
    static _Obj *__STL_VOLATILE _S_free_list[];
    // 类外定义,提到这里方便观察
    /* __default_alloc_template<__threads, __inst> ::_S_free_list[_NFREELISTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; */

    // 计算该区块应在哪个 free-list 中拿/还
    static size_t _S_freelist_index(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN - 1) / (size_t)_ALIGN - 1);
    }

    // Returns an object of size __n, and optionally adds to size __n free list.
    // 返回一个大小为 n 的对象,并且可能加入大小为 n 的其他区块到 free-list 中
    // (说人话就是一次性可能多申请一些内存块,避免多次申请,如果内存池大小不够就最少先返回一个)
    static void *_S_refill(size_t __n);

    // Allocates a chunk for nobjs of size size. nobjs may be reduced
    // if it is inconvenient to allocate the requested number.
    // 向堆申请 njobs 个 size 大小的内存块,njobs 在内存不足的情况下可能会降低
    static char *_S_chunk_alloc(size_t __size, int &__nobjs);

    // Chunk allocation state.
    // 标定内存池的起始位置,只会在_S_chunk_alloc 中改变
    static char *_S_start_free;
    static char *_S_end_free;
    static size_t _S_heap_size;

    // 初值设定 本来设定在类外,这里提上来方便观察
    /* template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;
       template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;
       template <bool __threads, int __inst> size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0; */

public:
    /* __n must be > 0 */
    // 内存申请
    static void *allocate(size_t __n) {
        void *__ret = 0;
        // 大于 128byte 直接去调用第一级配置器
        if (__n > (size_t)_MAX_BYTES) {
            __ret = malloc_alloc::allocate(__n);
        } else {
            // 计算 free-list 对应内存块的起始地址
            _Obj *__STL_VOLATILE *__my_free_list = _S_free_list + _S_freelist_index(__n);
            // 取出第一个内存块
            _Obj *__RESTRICT __result = *__my_free_list;
            if (__result == 0) // 如果一个内存块都没有,则直接去内存池中申请
                __ret = _S_refill(_S_round_up(__n));
            else {
                // 将剩下的内存块连起来返回结果
                *__my_free_list = __result->_M_free_list_link;
                __ret = __result;
            }
        }
        return __ret;
    }

    /* __p may not be 0 */
    // 内存归还
    static void deallocate(void *__p, size_t __n) {
        // 大于 128 直接走第一级配置器
        if (__n > (size_t)_MAX_BYTES) malloc_alloc::deallocate(__p, __n);
        else {
            // 查找 index
            _Obj *__STL_VOLATILE *__my_free_list = _S_free_list + _S_freelist_index(__n);
            _Obj *__q = (_Obj *)__p;
            // 头插到自由链表
            __q->_M_free_list_link = *__my_free_list;
            *__my_free_list = __q;
        }
    }

    static void *reallocate(void *__p, size_t __old_sz, size_t __new_sz);
};

// ========================== 类外定义的函数 ==========================
/* Returns an object of size __n, and optionally adds to size __n free list.
/* We assume that __n is properly aligned.
/* We hold the allocation lock.
*/
// 在 free-list 上没有空余的小块内存时调用,调用后至少可以给 1 个内存对象 -> 与内存池打交道
template<bool __threads, int __inst>
void *__default_alloc_template<__threads, __inst>::_S_refill(size_t __n) {
    // 默认一次取 20 个,但是如果内存空间不够的话可能会减少
    int __nobjs = 20;
    // 获取 njobs 个大小为 n 的对象
    char *__chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj *__STL_VOLATILE *__my_free_list;
    _Obj *__result;
    _Obj *__current_obj;
    _Obj *__next_obj;
    int __i;

    // 如果只申请到一个就直接返回
    if (1 == __nobjs) return (__chunk);

    // 申请到不止一个,则将第一个返回,其余的挂到对应位置的自由链表上
    __my_free_list = _S_free_list + _S_freelist_index(__n);
    /* Build free list in chunk */
    __result = (_Obj *)__chunk;
    *__my_free_list = __next_obj = (_Obj *)(__chunk + __n);
    for (__i = 1;; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj *)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj->_M_free_list_link = 0;
            break;
        } else {
            __current_obj->_M_free_list_link = __next_obj;
        }
    }
    return (__result);
}

// 内存池,与系统的堆空间打交道
template<bool __threads, int __inst>
char *__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int &__nobjs) {
    char *__result;
    // 计算总共需要多大内存空间
    size_t __total_bytes = __size * __nobjs;
    // 剩余内存空间
    size_t __bytes_left = _S_end_free - _S_start_free;

    // 如果剩余空间大于所需空间则直接返回
    if (__bytes_left >= __total_bytes) {
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return (__result);
    } else if (__bytes_left >= __size) {
        // 如果不足所需空间则计算还可以支持多少个对象,将可以返回的最大对象个数空间返回
        __nobjs = (int)(__bytes_left / __size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return (__result);
    } else {
        // 实在是空了,连一个对象的空间都没有了,则给内存池扩容
        // 扩容的大小是这次索取总请求的两倍加一些附加值
        size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);

        // Try to make use of the left-over piece.
        // 如果内存池中还有一些空间,则试图将它挂到对应大小的自由链表上
        if (__bytes_left > 0) {
            _Obj *__STL_VOLATILE *__my_free_list = _S_free_list + _S_freelist_index(__bytes_left);
            ((_Obj *)_S_start_free)->_M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj *)_S_start_free;
        }

        // 从堆上申请空间
        _S_start_free = (char*)malloc(__bytes_to_get);
        // 堆空间不足,malloc 失败
        if (0 == _S_start_free) {
            size_t __i;
            _Obj *__STL_VOLATILE *__my_free_list;
            _Obj *__p;
            // Try to make do with what we have. That can't
            // hurt. We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            // 从一个对象大小开始遍历每个自由链表的槽位,看看有没有空闲的内存块能用得上
            for (__i = __size; __i <= (size_t)_MAX_BYTES; __i += (size_t)_ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    // 如果找到空闲的内存块则更新
                    *__my_free_list = __p->_M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    // 递归调用自己,修正 njobs
                    return (_S_chunk_alloc(__size, __nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
            // 实在是啥都没有了,再尝试调用第一级配置器,如果用户配置了 oom-handle,看看能不能发力
            // 否则直接抛出 bad_alloc 了
            _S_end_free = 0; // In case of exception.
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
            // This should either throw an
            // exception or remedy the situation. Thus we assume it
            // succeeded.
        }
        // 申请到内存->更新内存池的大小
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
        // 递归调用自己
        return (_S_chunk_alloc(__size, __nobjs));
    }
}

template<bool threads, int inst>
void *__default_alloc_template<threads, inst>::reallocate(void *__p, size_t __old_sz, size_t __new_sz) {
    void *__result;
    size_t __copy_sz;
    if (__old_sz > (size_t)_MAX_BYTES && __new_sz > (size_t)_MAX_BYTES) {
        return (realloc(__p, __new_sz));
    }
    if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return (__p);
    __result = allocate(__new_sz);
    __copy_sz = __new_sz > __old_sz ? __old_sz : __new_sz;
    memcpy(__result, __p, __copy_sz);
    deallocate(__p, __old_sz);
    return (__result);
}

以上便是第二级配置器的完整实现。

uninitialized_xx

STL 定义五个全局函数,作用于未初始化的空间上,前两个函数是 construct() 和 destroy(),剩下的三个就是 uninitialized_xx 类型函数,具体分为:uninitialized_copy(), uninitialized_fill(), uninitialized_fill_n()。

uninitialized_copy()

该函数接收三个参数,迭代器 first, last, result。first 和 last 指向输出的起始和结尾,result 指向输出端。

// 属于 POD 类型
template<class _InputIter, class _ForwardIter>
inline _ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last, _ForwardIter __result, __true_type) {
    return copy(__first, __last, __result);
}

// 不属于 POD 类型
template<class _InputIter, class _ForwardIter>
_ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last, _ForwardIter __result, __false_type) {
    _ForwardIter __cur = __result;
    __STL_TRY {
        for (; __first != __last; ++__first, ++__cur)
            _Construct(&*__cur, *__first);
        return __cur;
    }
    __STL_UNWIND(_Destroy(__result, __cur));
}

template<class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter __uninitialized_copy(_InputIter __first, _InputIter __last, _ForwardIter __result, _Tp *) {
    // 在<type_traits>中有实现,与 iterator_traits<T, Alloc> 类似
    typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
    return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());
}

template<class _InputIter, class _ForwardIter>
inline _ForwardIter uninitialized_copy(_InputIter __first, _InputIter __last, _ForwardIter __result) {
    // 使用 traits 来获取迭代器类型,如果是 POD(Plain Old Data),即原生类型就直接调用高阶函数批量处理
    // 否则只能使用循环 + constructr() 的方式循环构造
    return __uninitialized_copy(__first, __last, __result, __VALUE_TYPE(__result));
}

// 对 char* 和 wchar_t 特化一个最有效率的做法 memmove(直击诶移动内存内容)
inline char *uninitialized_copy(const char *__first, const char *__last, char *__result) {
    memmove(__result, __first, __last - __first);
    return __result + (__last - __first);
}

inline wchar_t *uninitialized_copy(const wchar_t *__first, const wchar_t *__last, wchar_t *__result) {
    memmove(__result, __first, sizeof(wchar_t) * (__last - __first));
    return __result + (__last - __first);
}

uninitialized_fill()

template<class _ForwardIter, class _Tp>
inline void __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last, const _Tp &__x, __true_type) {
    fill(__first, __last, __x);
}

template<class _ForwardIter, class _Tp>
void __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last, const _Tp &__x, __false_type) {
    _ForwardIter __cur = __first;
    __STL_TRY {
        for (; __cur != __last; ++__cur)
            _Construct(&*__cur, __x);
    }
    __STL_UNWIND(_Destroy(__first, __cur));
}

template<class _ForwardIter, class _Tp, class _Tp1>
inline void __uninitialized_fill(_ForwardIter __first, _ForwardIter __last, const _Tp &__x, _Tp1 *) {
    typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
    __uninitialized_fill_aux(__first, __last, __x, _Is_POD());
}

template<class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first, _ForwardIter __last, const _Tp &__x) {
    __uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));
}

uninitialized_fill_n()

template<class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n, const _Tp &__x, __true_type) {
    return fill_n(__first, __n, __x);
}

template<class _ForwardIter, class _Size, class _Tp>
_ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n, const _Tp &__x, __false_type) {
    _ForwardIter __cur = __first;
    __STL_TRY {
        for (; __n > 0; --__n, ++__cur)
            _Construct(&*__cur, __x);
        return __cur;
    }
    __STL_UNWIND(_Destroy(__first, __cur));
}

template<class _ForwardIter, class _Size, class _Tp, class _Tp1>
inline _ForwardIter __uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp &__x, _Tp1 *) {
    typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
    return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
}

template<class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp &__x) {
    return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first));
}

综上所述,uninitialized_*() 类别的函数执行流程如下:

  1. 在 uninitialized_*() 中执行 __uninitialized_*()。
  2. 在 __uninitialized_*() 中判断传入迭代器类型进行判断。
  3. 如果是 POD 则使用高阶函数 (特化 char*, wchar*,使用 memmove() 直接进行内存移动)。
  4. 如果非 POD 则使用循环 + constructor() 构造对象。

目录

  1. 空间配置器 (Allocator 的标准接口)
  2. SGI 的特殊空间配置器 std::alloc
  3. 构造和析构的基本工具 construct() 和 destroy()
  4. 空间的配置和释放 std::alloc
  5. 第一级配置器 _mallocalloc_template 剖析
  6. 第二级配置器 _defaultalloc_template
  7. uninitialized_xx
  8. uninitialized_copy()
  9. uninitialized_fill()
  10. uninitializedfilln()
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • macOS 安装 Claude Code 完整教程
  • Mistral AI 发布开源多模态模型 Pixtral Large 并升级 le Chat
  • 动态规划专题:回文串问题与区间 DP
  • AirSim 无人机仿真入门:实现起飞与降落控制
  • Vivado FPGA 开发工具安装与配置教程
  • ChatBox AI:多模型多模态交互与 MCP 协议集成指南
  • 前端请求后端 404/405/500 状态码排查与解决指南
  • 哈希算法详解:原理、实现与安全应用
  • Open WebUI Docker 部署指南与最佳实践
  • 基于机器学习的 Web 入侵检测系统设计与实现
  • OpenClaw 框架更新:三天三版支持 GPT-5.4 与记忆热插拔
  • Rust 安装与环境配置教程
  • Diff-eRank:基于有效秩的大模型去噪能力评估新指标
  • Linux 内核源码下载指南:官方源、镜像加速与完整性校验
  • Lychee-Rerank-MM 本地部署教程:无网依赖图文重排序
  • Spring Boot 安全认证与授权核心解析
  • 深度学习环境搭建指南:PyTorch、Anaconda 与 GPU 配置
  • 开源电路板查看器 OpenBoardView:.brd 文件解析工具
  • Java 智慧养老系统:护理代办与陪诊全场景覆盖
  • Python 合并两个字典的 8 种常用方法

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online