As we all kown,C++中的STL容器使用其内置的std::allocator分配内存。如果我们想要更改STL分配内存的行为,我们不用更改容器的逻辑,只需要更改传入的allocator即可。一直以来,allocator对我来讲都笼罩着一层迷雾,总感觉是一个很复杂很恐怖的东西,最近因为课程原因,需要使用平台特定的内存管理函数(在NVM仿真平台quartz中需要使用其提供的pmalloc和pfree函数管理非易失性内存),就研究了一下std::allocator是怎么实现的。但是认真了解以后发现没有什么神奇的东西。
allocator主要有四个接口:
T *allocate(size_t):用void * ::operator new(size_t)分配内存(但是不构造,其实就是对malloc的封装)void deallocate(T*, size_t):使用void ::operator delete(void *)释放内存(其实是对free的封装)void construct(T*, Args&&):使用定位new(placement new::new(void*) T(std::forward(args)...) )在指定内存上进行构造void destroy(T*):调用析构函数(p->~T())进行析构不过后两者在C++17中被废弃了,在C++20中被删除了,我在stackoverflow上看到一个回答的大概意思是以后应该用std::allocator_traits::construct了。原回答:Why are are std::allocator’s construct and destroy functions deprecated in c++17?
去看了一下STL 的源码,发现std::allocator继承了__allocator_base,然后后者又继承了__gnu_cxx::new_allocator,也就是说其实用的是GNU版本的allocator,我认真阅读了一下__gnu_cxx::new_allocator的实现,发现的确挺简洁,没有什么特殊的东西,通过把里面的宏改成普通的关键字,删除掉我觉得没有什么用的版本判断,就可以得到一个简单的allocator实现:
// Copyright(C), Edward-Elric233
// Author: Edward-Elric233
// Version: 1.0
// Date: 2022/10/25
// Description: __gnu__cxx::new_allocator
#ifndef TEMP_PALLOCATOR_H
#define TEMP_PALLOCATOR_H#include "utils.h"
#include
#include namespace edward
{using std::size_t;using std::ptrdiff_t;/*** @brief An allocator that uses global new, as per [20.4].* @ingroup allocators** This is precisely the allocator defined in the C++ Standard.* - all allocation calls operator new* - all deallocation calls operator delete** @tparam T Type of allocated object.*/templateclass pallocator{public:typedef size_t size_type;typedef ptrdiff_t difference_type;typedef T* pointer;typedef const T* const_pointer;typedef T& reference;typedef const T& const_reference;typedef T value_type;templateusing other = pallocator;// _GLIBCXX_RESOLVE_LIB_DEFECTS// 2103. propagate_on_container_move_assignmenttypedef std::true_type propagate_on_container_move_assignment;constexpr pallocator() noexcept { }constexprpallocator(const pallocator&) noexcept { }templateconstexprpallocator(const pallocator&) noexcept { }~pallocator() noexcept { }pointeraddress(reference __x) const noexcept{ return std::__addressof(__x); }const_pointeraddress(const_reference __x) const noexcept{ return std::__addressof(__x); }// NB: __n is permitted to be 0. The C++ standard says nothing// about what the return value is when __n == 0.[[nodiscard]] pointer //cannot discard return value!!!allocate(size_type __n, const void* = static_cast(0)){if (__n > this->max_size())throw std::bad_alloc();print("allocate", __n * sizeof(T), "bytes");return static_cast(::operator new(__n * sizeof(T)));}// __p is not permitted to be a null pointer.voiddeallocate(pointer __p, size_type __n){print("deallocate", __n * sizeof(T), "bytes");::operator delete(__p);}size_typemax_size() const noexcept{return size_t(__PTRDIFF_MAX__) / sizeof(T);}templatevoidconstruct(_Up* __p, _Args&&... __args)noexcept(std::is_nothrow_constructible<_Up, _Args...>::value){ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }templatevoiddestroy(_Up* __p)noexcept(std::is_nothrow_destructible<_Up>::value){ __p->~_Up(); }templatefriend booloperator==(const pallocator&, const pallocator<_Up>&) noexcept{ return true; }templatefriend booloperator!=(const pallocator&, const pallocator<_Up>&) noexcept{ return false; }};} // namespace#endif //TEMP_PALLOCATOR_H
我在allocate和deallocate函数中添加了打印函数,utils.h头文件的获取可以看一下我之前的博客:C++ 工具函数库