目录
在讲内存分配之前,先对一些基础概念进行阐述,以便能更好的讨论问题
代码编译为可执行程序后运行占用的内存可以分为以下几个区域:
1.栈区(stack) :? 由编译器自动分配释放,用于存放函数的参数值,局部变量的值等;在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用;栈上的内存在函数返回时就会自动释放;栈区的内存地址延伸方式从高地址向地址;栈内存的大小通常是有限的,所以大量使用可能导致栈溢出。
2.堆区(heap) : 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收;堆可以动态地扩展和收缩,注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3.全局区(静态区、static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4.文字常量区 : 常量字符串就是放在这里的,程序结束后由系统释放。
5. 程序代码区 :?存放函数体的二进制代码,代码里面的全局函数和类成员函数编译后就是存在这个区域。
一个可执行程序,在不运行时占用磁盘空间的是全局区、文字常量区和程序代码区,运行后栈区和堆区在内存里面才划分出来工作。下面是区域划分图:
C/C++内存分配方式有3种:
1.从静态存储区域分配:? 是在程序编译和链接时就确定好的内存;这些内存在程序整个运行期间都存在,如全局变量,static变量等。
2.在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限,如数组,局部变量,形参。?
3.从堆上分配:亦称动态内存分配 ,是在程序加载、调入、执行的时候分配/回收的内存;程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定 ,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
在C++里面这种方式是用的最多的,代码如下:
template <class T>
struct CreateUsingNew
{
template <typename... Args>
static T* Create(Args... args)
{ return new T(std::forward<Args>(args)...); }
static void Destroy(T* p)
{ delete p; }
};
在STL中空间配置器的4个函数:
内存的配置:alloc::allocate();
对象的构造:::construct();
对象的析构:::destroy();
内存的释放:alloc::deallocate();
?利用空间分配器分配内存的代码如下:
template<template<class> class Alloc>
struct CreateUsing
{
template <class T>
struct Allocator
{
static Alloc<T> allocator;
template <typename... Args>
static T* Create(Args... args)
{
return new (allocator.allocate(1)) T(std::forward<Args>(args)...);
}
static void Destroy(T* p)
{
//allocator.destroy(p);
p->~T();
allocator.deallocate(p,1);
}
};
};
?C语言的传统方式分配和释放内存,代码如下:
template <class T>
struct CreateUsingMalloc
{
template <typename... Args>
static T* Create(Args... args)
{
void* p = std::malloc(sizeof(T));
if (!p) return 0;
return new(p) T(std::forward<Args>(args)...);
}
static void Destroy(T* p)
{
p->~T();
std::free(p);
}
};
代码如下:
template <class T>
struct CreateStatic
{
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4121 )
// alignment of a member was sensitive to packing
#endif // _MSC_VER
union MaxAlign
{
char t_[sizeof(T)];
short int shortInt_;
int int_;
long int longInt_;
float float_;
double double_;
long double longDouble_;
struct Test;
int Test::* pMember_;
int (Test::*pMemberFn_)(int);
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif // _MSC_VER
template <typename... Args>
static T* Create(Args... args)
{
static MaxAlign staticMemory_;
return new(&staticMemory_) T(std::forward<Args>(args)...);
}
static void Destroy(T* p)
{
p->~T();
}
};
上面的例子都用了std::forward,实现了构造函数参数的完美转发,它的用法可以参考博客C++之std::forward-CSDN博客