小小STL实现教程的重点和详细步骤记录
借鉴候捷老师的《STL源码剖析》与Github上大佬 https://github.com/zouxiaohang/TinySTL/tree/master/TinySTL 的代码来编写一个基于SGI STL 的 TinySTL。
记录下编写过程中的难点重点与细节实现讨论。
STL规定配置器(allocator)定义在 <memory>。<memory> 中含有 <Alloc.h>, <Construct.h>两个文件。前者负责空间配置与释放,后者负责对象内容的构造与析构。
<Alloc.h>:定义一级 ,二级配置器
配置器设计哲学将考虑如下问题:
1.向system heap要求空间
2.考虑多线程(multi-threads)状态
3.考虑内存不足时的应变措施
4.考虑过多“小型区块”可能造成过多的内存碎片问题(fragment)问题
C++的内存配置基本操作是 ::operator new() 释放内存基本操作为::operator::delete()。这两个函数实际相当于C语言的 malloc() free() 。 而实际上SGI STL 就是以 malloc() 和 free()完成内存呢配置和释放的。
考虑到问题4. SGI采用双层配置器设计,一级配置器直接使用malloc() 和 free(),第二级配置器则视情况采用不同策略配置。
二级配置器配置策略和实现。
内存配置策略:内存按照块分配,如果客户申请区块够大(超过128bytes)则由一级配置器处理。
一级配置器allocate()和reallocate()函数在调用malloc()和realloc()失败后,调用oom_malloc()和oom_realloc(),oom_malloc()和oom_realloc()函数内都有内循环,不断的调用“内存不足处理例程”,期望在某次调用后可以获得足够的内存;但若内存不足处理例程客端没有设定,oom_malloc()和oom_realloc()会直接调用__THROW_BAD_ALLOC抛出bad_alloc异常信息,或者利用exit(1)直接终止程序。
当区块小于128bytes,则以内存池(memory pool)管理:每次配置一大块内存,并维护对应的*链表(free-lists),若下次再有相同大小的内存需求,直接从free-lists拔出,若客户返还小额区块,配置器同样回收到free-lists。为了方便管理二级配置器会主动将任何小额区块的内存需求量上调至8的倍数,并维护16个free-lists,各自管理8的倍数bytes的小额区块(8,16,32...128bytes)。
#ifndef _ALLOC_H_ #define _ALLOC_H_ #include<cstdlib> namespace TinySTL { /* 空间配置器,以字节数(Bytes)单位分配 考虑内存破碎问题,SIG采用双层配置器,第一层配置器直接使用malloc( ) and free( ) (源码剖析-候捷P57) SGI二级适配器设计了一些机制,尽量避免造成过多内存碎片(源码剖析-候捷P60) */ class alloc { private: enum EAlign{ ALIGN = 8 }; //小型区块的上调边界(上调至8的倍数,例如客户端要求30bytes,则自动上调至32bytes) enum EMaxBytes{ MAXBYTES = 128 }; //小型区块大小上限,超过则由malloc( ) 分配 enum ENFreeLists { NFREELISTS = (EMaxBytes::MAXBYTES / EAlign::ALIGN) }; //free-list 个数 128/8 =16 个 enum ENObjs { NOBJS = 20 }; //每次增加的节点数 private: //free-lists 的结点结构,free-list一是指向下一个块空白内存,二是提供用户使用的一块内存(因为union) union obj { union obj* next; //可看作指针 指向空闲区块 char client[1]; //客户使用的内存块,若使用中,咋obj *next则不会指向这块内存 }; static obj* free_list[ENFreeLists::NFREELISTS]; //free-list 数组编号 //表示内存池 private: static char* start_free; //内存池起始位置,只在chunk_alloc( )中变化 static char* end_free; //结束位置 static size_t heap_size; private: //将bytes上调至8的倍数,尽量避免内存碎片化 static size_t ROUND_UP(size_t bytes) { return ((bytes + EAlign::ALIGN - 1) & ~(EAlign::ALIGN - 1)); } //根据区块大小决定使用地n号free-list,n从0开始计算 static size_t FREELIST_INDEX(size_t bytes) { return (((bytes)+EAlign::ALIGN - 1) / EAlign::ALIGN - 1); } //返回一个大小为n的对象,并可能 加入大小为n的其他区块到 free-list
//此函数准备为free-lists重新填充空间,新的空间曲子内存池(该部分由chunk_alloc()完成)
static void* refill(size_t n); //配置一大块空间,可容纳nobjs个大小为size的区块 //如果配置nobjs个区块有所不便,nobjs可能会降低 static char* chunk_alloc(size_t bytes); public: static void* allocate(size_t bytes); static void deallocate(void* ptr, size_t bytes); static void* reallocate(void* ptr, size_t old_sz, size_t new_sz); }; } #endif // !_ALLOC_H_
各个函数的具体实现细节
注意每一个函数的作用功能
#include "Alloc.h" namespace TinySTL { //alloc class initialization char* alloc::start_free = 0; char* alloc::end_free = 0; size_t alloc::heap_size = 0; //initialize 16 free-lists alloc::obj* alloc::free_list[alloc::ENFreeLists::NFREELISTS] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; void* alloc::allocate(size_t bytes) { // 如果大于128bytes ,调用一级适配器,即直接malloc if (bytes > EMaxBytes::MAXBYTES) { return malloc(bytes); } //寻找16个free-list中合适的一个 size_t index = FREELIST_INDEX(bytes); obj* list = free_list[index]; //list 为找到的那个free-list if (list) {//找到的free-list还有空间 free_list[index] = list->next; //调整list指向下一块空闲内存块,客户端使用的区块free-list将不再指向它们 return list; } else { // 此list无足够空间,需要从内存池里面取,调用refill() return refill(ROUND_UP(bytes)); } } void alloc::deallocate(void* ptr, size_t bytes) { //大于128bytes则用一级配置器 直接free() if (bytes > EMaxBytes::MAXBYTES) { free(ptr); } else { //寻找对应的free-list,调整free-list 区块回收 size_t index = FREELIST_INDEX(bytes); obj* node = static_cast<obj*>(ptr); node->next = free_list[index]; //调整free-list回收区块 free_list[index] = node; //回收的区块是挂接到free-list 对应区块的第一位置,而不是添加到尾端 } } //重新分配 void* alloc::reallocate(void* ptr, size_t old_sz, size_t new_sz) { deallocate(ptr, old_sz); ptr = allocate(new_sz); return ptr; } //当发现free-list无可用区块时,调用refill()准备重新填充空间,新的空间将从内存池取(该部分由chunk_alloc()完成) //返回一个大小为n的对象,并且有时候会为适当的 free-list 增加节点 //假设bytes已经调整为8的倍数 void* alloc::refill(size_t bytes) { size_t nobjs = ENObjs::NOBJS; //从内存池取内存 char *chunk = chunk_alloc(bytes, nobjs); //尝试取得nobjs个区块作为free-list 的新结点 ,nobjs 是pass by reference obj** my_free_list = 0; obj *result = 0; obj* current_obj = 0 , *next_obj = 0; if (nobjs == 1) { //只够一个区块的获取,分配给调用者,free-list无新节点 return chunk; } else { //否则准备调整free-list,纳入新结点 my_free_list = free_list + FREELIST_INDEX(bytes); result = (obj*)(chunk); //这一块准备返回给客户端 *my_free_list = next_obj = (obj*)(chunk + bytes); //引导free-list指向新配置的空间,bytes是用户申请的内存被上调为8倍数的大下 //以下将free-list的各个结点串接起来 for (int i = 1;; ++i) { //从1开始,因为第0个区块要返回给客户端 current_obj = next_obj; next_obj = (obj*)((char*)next_obj + bytes); //链接指向下一新节点 if (nobjs - 1 == i) { //最后一个区块 current_obj->next = 0; break; } else { current_obj->next = next_obj; } } return result; } } //此函数功能是从内存池取出空间 char* alloc::chunk_alloc(size_t bytes, size_t & nobjs) { char* result = 0; size_t total_bytes = bytes * nobjs; //块数 * 块大小 size_t bytes_left = end_free - start_free; if (bytes_left >= total_bytes) { result = start_free; start_free = start_free + total_bytes; return result ; } else if (bytes_left >= bytes) { //内存剩余空间不满足需要,但足够供应一个或者以上的区块内存 nobjs = bytes_left / bytes; total_bytes = nobjs * bytes; result = start_free; start_free += total_bytes; return result; } else { //剩余空间甚至不足一个区块大小 size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //内存池可能残余一些零头 (内存),先配给适当的free-list if (bytes_left > 0) { obj** my_free_list = free_list + FREELIST_INDEX(bytes_left); //调整free-list,将内存池参与空间编入 ((obj*)start_free)->next = *my_free_list; *my_free_list = (obj*)start_free; } //配置heap空间来补充内存池 start_free = (char*)malloc(bytes_to_get); //如果heap空间不足,malloc失败 if (!start_free) { obj** my_free_list = 0, * p = 0; for (int i = 0; i <= EMaxBytes::MAXBYTES; i += EAlign::ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); //free-list的index p = *my_free_list; //free-list 内未使用的区块,调整free-list释放出未使用区块 if (p != 0) { *my_free_list = p->next; start_free = (char*)p; end_free = start_free + i; //递归调用 修正nobjs (块数) return chunk_alloc(bytes, nobjs); } } end_free = 0; } heap_size += bytes_to_get; end_free = start_free + bytes_to_get; //递归调用 修正nobjs return chunk_alloc(bytes, nobjs); } } }
原文地址:https://www.cnblogs.com/Reven-Red/p/14387453.html
推荐阅读
-
[姿势估计] 实践记录:使用 Dlib 和 mediapipe 进行人脸姿势估计 - 本文重点介绍方法 2):方法 1:基于深度学习的方法:。 基于深度学习的方法:基于深度学习的方法利用深度学习模型,如卷积神经网络(CNN)或递归神经网络(RNN),直接从人脸图像中学习姿势估计。这些方法能够学习更复杂的特征表征,并在大规模数据集上取得优异的性能。方法二:基于二维校准信息估计三维姿态信息(计算机视觉 PnP 问题)。 特征点定位:人脸姿态估计的第一步是通过特征点定位来检测和定位人脸的关键点,如眼睛、鼻子和嘴巴。这些关键点提供了人脸的局部结构信息,可用于后续的姿势估计。 旋转表示:常见的旋转表示方法包括欧拉角和旋转矩阵。欧拉角通过三个旋转角度(通常是俯仰、偏航和滚动)描述头部的旋转姿态。旋转矩阵是一个 3x3 矩阵,表示头部从一个坐标系到另一个坐标系的变换。 三维模型重建:根据特征点的定位结果,三维人脸模型可用于姿势估计。通过将人脸的二维图像映射到三维模型上,可以估算出人脸的旋转和平移信息。这就需要建立人脸的三维模型,然后通过优化方法将模型与特征点对齐,从而获得姿势估计结果。 特征点定位 特征点定位是用于检测人脸关键部位的五官基础部分,还有其他更多的特征点表示方法,大家可以参考我上一篇文章中介绍的特征点检测方案实践:人脸校正二次定位操作来解决人脸校正的问题,客户在检测关键点的代码上略有修改,坐标转换部分客户见上图 def get_face_info(image). img_copy = image.copy image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_detection.process(image) # 在图像上绘制人脸检测注释。 image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) box_info, facial = None, None if results.detections: for detection in results. for detection in results.detections: mp_drawing.Drawing.detection = 无 mp_drawing.draw_detection(image, detection) 面部 = detection.location_data.relative_keypoints 返回面部 在上述代码中,返回的数据是五官(6 个关键点的坐标),这是用 mediapipe 库实现的,下面我们可以尝试用另一个库:dlib 来实现。 使用 dlib 使用 Dlib 库在 Python 中实现人脸关键点检测的步骤如下: 确保已安装 Dlib 库,可使用以下命令: pip install dlib 导入必要的库: 加载 Dlib 的人脸检测器和关键点检测器模型: 读取图像并将其灰度化: 使用人脸检测器检测图像中的人脸: 对检测到的人脸进行遍历,并使用关键点检测器检测人脸关键点: 显示绘制了关键点的图像: 以下代码将参数 landmarks_part 添加到要返回的关键点坐标中。
-
小小STL实现教程的重点和详细步骤记录