在stl里面,stl对容器中元素内存的分配,与元素的构造、析构过程进行了分离
c++ 在全局定义了 operator new(size_t size), 调用malloc(size)
operator new(size_t size,void* buf) 什么也不做直接return buf
operator delete(void* buf); 调用free(buf)
对于ClassA* ptr = new ClassA(),编译器步骤为(分配内存+调用构造函数)
void * mem=operator new(sizeof(ClassA));
ClassA* ptr=(ClassA*)mem;
ptr->ClassA::ClassA();
对于new(_mem) ClassA(), 编译器步骤为(使用已分配的内存(内存需要对齐) + 调用构造函数,相当于分离分配过程)
void* mem = operator new(sizeof(ClassA),_mem); //其实什么也没做,直接返回mem
ClassA* ptr=(ClassA*)mem;
ptr->ClassA::ClassA(); //通过指针直接调用时,无法编译
可以使用stl里面实现的标准内存分配器std::allocator
在windows上其使用_Default_allocate_traits实现空间分配,比较直接使用 operator new直接分配,施放则是直接调用_Deallocate函数使用operator delete释放
在linux上是使用new_allocator类进行分配、释放,也是直接operator new、operator delete
allocator<Foo> alloc;
//Allocate memory for one or n objects,根据Foo对象内存对齐后的原始内存
auto p = alloc.allocate(1);
//Construct an object of Foo on allocated memory block p, by calling one of Foo's constructors
alloc.construct(p, args, ...);
//OK, p now points to a Foo object ready for use...
//Call Foo's destructor but don't release memory of p
alloc.destroy(p);
//Release memory
alloc.deallocate(p, 1);
在linux上,编译器版本为9.4.0扩展了mt_alloc,适用于单线程、多线程,具有内存池功能; 分为一级管理与二级管理,一级管理直接new、delete,二级管理采用内存池。默认情况大于128bytes使用一级管理,小于采用二级管理;当采用二级管理时,每次申请都是先查询释放链表有没有释放过的,有释放过的先复用,然后才是在内存池中割一块新的,或者重新分配一块
#include <ext/mt+allocator.h>
int main(argc , char **argv)
{
//__ommon_pool_policy模板参数bool表示是否用于多线程
typedef __gnu__cxx::__mt_alloc<<XXXX>, __gnu_cxx::__common_pool_policy<__gnu_cxx::_pool, false>> MtPoolAlloc;
typedef __gnu_cxx::__pool_base::_Tune MtPoolAllocTune;
MtPoolAlloc alloc;
/*
size_t _M_align; //字节对齐
size_t _M_max_bytes; //默认为128,表示128字节以上的用new直接分配
size_t _M_min_bin;//可分配的最小的内存大小,默认为8
size_t _M_chunk_size;//每次从os申请的内存块的大小,默认为4096 - 4 *sizeof(void*)
size_t _M_max_threads;//可支持的最多的线程数,默认4096
size_t _M_freelist_headroom;//单线程能保存的空闲块的百分比,默认10%
bool _M_force_new;//是否直接使用new和delete
*/
MtPoolAllocTune opt{8, sizeof(XXXX), sizeof(XXXX), 4096 - 4 * sizeof(void*), 1, 10, false};
//alloc._M_get_options可以返回正在使用的,修改部分也可以
alloc._M_set_options(opt);
auto *tt1 = alloc.allocate(1);
alloc.construct(tt1, 1, 2);//调用构造函数
std::cout << reinterpret_cast<unsigned long long>((void*)tt1) << std::endl;
alloc.destroy(tt1); //调用析构函数; 如果析构是虚函数,没有调用构造函数的话,这里会崩溃
alloc.deallocate(tt1, 1); //放回内存池
tt1 = alloc.allocate(1);
alloc.construct(tt1, 1, 2);//调用构造函数
std::cout << reinterpret_cast<unsigned long long>((void*)tt1) << std::endl;
alloc.destroy(tt1);
alloc.deallocate(tt1, 1); //放回内存池
}
运行结果为:
94504784214136
XXXX() //构造函数打印
~XXXX() //析构函数打钱
94504784214136
XXXX() //构造函数打印
~XXXX() //析构函数打钱
与allocator这一类分配器,相配对的还有专门的stl算法函数,如uninitialized_copy、uninitialized_copy_n、uninitialized_fill、uninitialized_fill_n等,都是把输入源拷贝到已分配(但未初始化)的内存上,在对应位置直接调用对应的类的拷贝构造函数
自定义allocator,范例如下
#include <vector>
#include <ext/mt_allocator.h>
template<typename T>
class SelfAlloc{
public:
typedef T value_type;
SelfAlloc() = default;
template<typename T1>
SelfAlloc(const SelfAlloc<T1>& other){}
T* address(T &x){
return static_cast<T*>(&x);
}
const T* address(const T &x){
return static_cast<const T*>(&x);
}
T* allocate(size_t _n,const void* hit=0 ){
return static_cast<T*>(::operator new(_n*sizeof(T)));
}
void deallocate(T* p, size_t _n){
::operator delete(p);
}
size_t max_size()const throw(){
return static_cast<size_t>(std::numeric_limits<int>::max()/sizeof(T));
}
template<typename _Up, typename... _Args>
void construct(_Up* __p, _Args&&... __args){
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
}
template<typename _Up>
void destroy(_Up* __p){
__p->~_Up();
}
};
template<typename T>
inline bool operator==(const SelfAlloc<T> &, const SelfAlloc<T> &){
return true;
}
template<typename T>
inline bool operator!=(const SelfAlloc<T> &, const SelfAlloc<T> &){
return false;
}
int main(int argc, char **argv){
std::vector<int, SelfAlloc<int>> ewefwi;
ewefwi.push_back(2);
ewefwi.push_back('a');
auto wefwff = ewefwi;
std::vector<int, SelfAlloc<int>> jlllll;
jlllll = ewefwi;
return 0;
}