【C++学习】STL之空间配置器之一级空间配置器

文章目录

    • 📊什么是空间配置器
    • ✈STL 提供六大组件的了解
    • 👀为什么需要空间配置器
    • 👍SGI-STL空间配置器实现原理
      • 🌂一级空间配置器的实现

        📊什么是空间配置器

        空间配置器,顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的,在默默地工作。

        ✈STL 提供六大组件的了解

        • 容器(containers):各种数据结构,如 vector, list, deque, set, map 用来存放数据。从实现的角度来看,STL 容器是一种 class template。
        • 算法(algorithms):各种常用的算法如 sort, search, copy, erase…从实现角度来看,STL 算法是一种 function template。
        • 迭代器(iterators):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”。从实现角度来看,迭代器是一种将 operator *, operator ->, operator++, operator– 等指针相关操作予以重载的class template。
        • 仿函数(functors):行为类似函数,可以作为算法的某种策略。从实现角度来看,仿函数是一种重载了 operator() 的 class 或class template。
        • 适配器(adapters):一种用来修饰容器或仿函数或迭代器接口的东西。例如 STL 提供的 queue 和 stack,虽然看似容器,其实只能算是一种容器适配器,因为它们的底部完全借助 deque,所有操作都由底层的 deque 供应。
        • 配置器(allocator):负责空间配置与管理,从实现角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的 class template。

          👀为什么需要空间配置器

          • 从使用 STL 层面而言,空间配置器并不需要介绍 ,因为容器底层都给你包装好了,但若是从 STL 实现角度出发,空间配置器是首要理解的。

          • 作为 STL 设计背后的支撑,空间配置器总是在默默地付出着。为什么你可以使用算法来高效地处理数据,为什么你可以对容器进行各种操作,为什么你用迭代器可以遍历空间,这一切的一切,都有“空间配置器”的功劳。

            模拟实现vector、list、map、unordered_map等容器时,所有需要空间的地方都是通过new申请的,虽然代码可以正常运行,但是有以下不足之处:

            • 空间申请与释放需要用户自己管理,容易造成内存泄漏。
            • 频繁向系统申请小块内存块,容易造成内存碎片。
            • 频繁向系统申请小块内存,影响程序运行效率。
            • 直接使用malloc与new进行申请,每块空间前有额外空间浪费。
            • 申请空间失败怎么应对。
            • 代码结构比较混乱,代码复用率不高。
            • 未考虑线程安全问题。

              因此需要设计一块高效的内存管理机制。

              👍SGI-STL空间配置器实现原理

              以上提到的几点不足之处,最主要还是:频繁向系统申请小块内存造成的。那什么才算是小块内存?SGI-STL以128作为小块内存与大块内存的分界线,将空间配置器其分为两级结构,一级空间配置器处理大块内存,二级空间配置器处理小块内存。

              🌂一级空间配置器的实现

              • 一级空间配置器原理非常简单,直接对malloc与free进行了封装,并增加了C++中

                set_new_handle思想。

                实现代码:

                template class __malloc_alloc_template
                {private:
                 static void *oom_malloc(size_t);
                public:
                // 对malloc的封装
                 static void * allocate(size_t n)
                 { // 申请空间成功,直接返回,失败交由oom_malloc处理
                 void *result = malloc(n);
                 if (0 == result) 
                   result = oom_malloc(n);
                 return result;
                 }
                // 对free的封装
                 static void deallocate(void *p, size_t /* n */)
                 { free(p);}
                 // 模拟set_new_handle
                 // 该函数的参数为函数指针,返回值类型也为函数指针
                 // void (*   set_malloc_handler( void (*f)() ) )()
                 static void (* set_malloc_handler(void (*f)()))()
                 { void (* old)() = __malloc_alloc_oom_handler;
                 __malloc_alloc_oom_handler = f;
                 return(old);
                 }
                };
                // malloc申请空间失败时代用该函数
                template void * __malloc_alloc_template::oom_malloc(size_t n)
                {void (* my_malloc_handler)();
                void *result;
                for (;;) 
                { // 检测用户是否设置空间不足应对措施,如果没有设置,抛异常,模式new的方式
                   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);
                }
                }
                typedef __malloc_alloc_template<0> malloc_alloc;