第10篇:C++ 堆内存管理器-allocator
发布网友
发布时间:2024-09-29 06:08
我来回答
共1个回答
热心网友
时间:2024-10-01 23:49
分配器是负责封装堆内存管理的对象,它们在整个标准库中使用,特别是STL容器使用它们来管理容器内部的所有内存分配,大部分情况下,程序员不用理会,标准容器使用默认的分配器称为std::allocator。例如当你声明一个简单的vector对象时,C++编译器默认已经使用了内置的std::allocator。在标准库的vector模板当中,第二个模板参数_Alloc就是std::allocator,实际上,std::allocator也是一个类模板。
在历史背景中,Alexander Stepanov提出了分配器的想法,其动机是使容器完全独立于底层内存模型。他打算让分配器完全封装内存模型,但标准委员会发现这很危险,因为这种方法会导致不可接受的效率下降。因此,分配器的当前用途是让程序员控制容器内的内存分配,而不是采用底层硬件的地址模型。
本文将从零实现自己的内存分配器,理解std::allocator的内部运行机制,自定义allocator有很多现实的原因。每个容器实例中都有一个Allocator实例,它向分配器请求存储来存储元素。分配器应具备的基本成员函数如下:
标准库中的allocator还有一些可选的成员,视不同C++编译器版本而异。接下来是MyAllocator的实现,首先在调用层代码中,声明一个vector实例,显式传入我们自定义的MyAllocator,并使用他来分配内存。
以下是MyAllocator的定义,并定义了标准库规定的所有类型别名,这里列出所有类型别名仅仅为了完整说明一个allocator的实现过程。接下来是allocator的堆内存管理接口,需要实现的是allocate成员函数和dealloc成员函数,整个allocator的堆内存管理都围绕这两个接口展开。
在MyAllocator实现的allocate和deallocate函数是必须的,同时定义了另一个allocate版本的成员函数,接受一个numObjs参数,并接受一个已分配堆内存的指针,他是一个只想最近分配的元素的指针。可以使用他来改进已分配内存的释放,但在我们的示例中,我们会忽略它并立即返回。
此外,构造和销毁对象的函数也在MyAllocator中实现,这些函数在C++20中已被废弃,但为了完整性,这里仍然提及。在C++20之前的标准库中,我们有construct和destroy接口,用于在我们分配的内存中构造类型T的实际对象。当调用new操作符分配了原始内存,new操作符并不会执行任何类型T的初始化。
在MyAllocator实现中,构造函数和销毁函数的运行原理非常简单,它们在内部调用类型T的构造函数,并转发外部的任意参数传递给类型T的构造函数。同样,destroy接口实现如下,这两个函数默认情况下是完全可选,这里展示如何完整的MyAllocator的自定义实现。
内置绑定机制是分配器的难点,它是一个类模板的成员。rebind内部类成员根据标准库的定义,rebind被定义为std::allocator类的结构成员,用于不同参数类型的分配器实例。当容器内部可能需要分配不同类型的对象,例如std::list时,分配器MyAllocator用于分配类型T的对象,而std::list实际上需要分配某个节点类型Node的对象。通常会用另为一个模板参数U来表示调用节点类型Node,std::list需要获得类型U对象的分配器,该分配器使用MyAllocator提供的分配机制。
在自定义的数据结构容器类的private作用域定义一个类型别名,以指定相应的类型。关于MyAllocator的全貌,其实现大概如下代码,若要确认该自定义的MyAllocator和std::allocator一样能够对所有std的容器起到作用,不妨在增加一些计数器的数据成员。
调用代码的基本形式如下,用vector执行下面的测试如下...
总结,本文通过原生实现的MyAllocator来说明std::allocator的内部机制,提供了一个全面的自定义实现示例。通过理解并实现自己的内存分配器,可以更深入地了解标准库中allocator的工作原理和设计。如有帮助,欢迎通过微信或支付宝支持作者。
热心网友
时间:2024-10-01 23:49
分配器是负责封装堆内存管理的对象,它们在整个标准库中使用,特别是STL容器使用它们来管理容器内部的所有内存分配,大部分情况下,程序员不用理会,标准容器使用默认的分配器称为std::allocator。例如当你声明一个简单的vector对象时,C++编译器默认已经使用了内置的std::allocator。在标准库的vector模板当中,第二个模板参数_Alloc就是std::allocator,实际上,std::allocator也是一个类模板。
在历史背景中,Alexander Stepanov提出了分配器的想法,其动机是使容器完全独立于底层内存模型。他打算让分配器完全封装内存模型,但标准委员会发现这很危险,因为这种方法会导致不可接受的效率下降。因此,分配器的当前用途是让程序员控制容器内的内存分配,而不是采用底层硬件的地址模型。
本文将从零实现自己的内存分配器,理解std::allocator的内部运行机制,自定义allocator有很多现实的原因。每个容器实例中都有一个Allocator实例,它向分配器请求存储来存储元素。分配器应具备的基本成员函数如下:
标准库中的allocator还有一些可选的成员,视不同C++编译器版本而异。接下来是MyAllocator的实现,首先在调用层代码中,声明一个vector实例,显式传入我们自定义的MyAllocator,并使用他来分配内存。
以下是MyAllocator的定义,并定义了标准库规定的所有类型别名,这里列出所有类型别名仅仅为了完整说明一个allocator的实现过程。接下来是allocator的堆内存管理接口,需要实现的是allocate成员函数和dealloc成员函数,整个allocator的堆内存管理都围绕这两个接口展开。
在MyAllocator实现的allocate和deallocate函数是必须的,同时定义了另一个allocate版本的成员函数,接受一个numObjs参数,并接受一个已分配堆内存的指针,他是一个只想最近分配的元素的指针。可以使用他来改进已分配内存的释放,但在我们的示例中,我们会忽略它并立即返回。
此外,构造和销毁对象的函数也在MyAllocator中实现,这些函数在C++20中已被废弃,但为了完整性,这里仍然提及。在C++20之前的标准库中,我们有construct和destroy接口,用于在我们分配的内存中构造类型T的实际对象。当调用new操作符分配了原始内存,new操作符并不会执行任何类型T的初始化。
在MyAllocator实现中,构造函数和销毁函数的运行原理非常简单,它们在内部调用类型T的构造函数,并转发外部的任意参数传递给类型T的构造函数。同样,destroy接口实现如下,这两个函数默认情况下是完全可选,这里展示如何完整的MyAllocator的自定义实现。
内置绑定机制是分配器的难点,它是一个类模板的成员。rebind内部类成员根据标准库的定义,rebind被定义为std::allocator类的结构成员,用于不同参数类型的分配器实例。当容器内部可能需要分配不同类型的对象,例如std::list时,分配器MyAllocator用于分配类型T的对象,而std::list实际上需要分配某个节点类型Node的对象。通常会用另为一个模板参数U来表示调用节点类型Node,std::list需要获得类型U对象的分配器,该分配器使用MyAllocator提供的分配机制。
在自定义的数据结构容器类的private作用域定义一个类型别名,以指定相应的类型。关于MyAllocator的全貌,其实现大概如下代码,若要确认该自定义的MyAllocator和std::allocator一样能够对所有std的容器起到作用,不妨在增加一些计数器的数据成员。
调用代码的基本形式如下,用vector执行下面的测试如下...
总结,本文通过原生实现的MyAllocator来说明std::allocator的内部机制,提供了一个全面的自定义实现示例。通过理解并实现自己的内存分配器,可以更深入地了解标准库中allocator的工作原理和设计。如有帮助,欢迎通过微信或支付宝支持作者。