目录
基础概念
内存布局
分配方式
实现
1.new和delete
2.利用空间配置器alloc
3.用malloc和free
4.静态内存分配
基础概念
在讲内存分配之前,先对一些基础概念进行阐述,以便能更好的讨论问题
内存布局
代码编译为可执行程序后运行占用的内存可以分为以下几个区域:
1.栈区(stack) : 由编译器自动分配释放,用于存放函数的参数值,局部变量的值等;在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用;栈上的内存在函数返回时就会自动释放;栈区的内存地址延伸方式从高地址向地址;栈内存的大小通常是有限的,所以大量使用可能导致栈溢出。
2.堆区(heap) : 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收;堆可以动态地扩展和收缩,注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3.全局区(静态区、static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4.文字常量区 : 常量字符串就是放在这里的,程序结束后由系统释放。
5. 程序代码区 : 存放函数体的二进制代码,代码里面的全局函数和类成员函数编译后就是存在这个区域。
一个可执行程序,在不运行时占用磁盘空间的是全局区、文字常量区和程序代码区,运行后栈区和堆区在内存里面才划分出来工作。下面是区域划分图:
实例分析
int g_a=0; //全局初始化区
static int g_b = 0; //全局变量,static限制了使用区域
char *g_p1; //全局未初始化区
static void func(int x, int y) //x,y是形参,栈区
{
static int x1 =0;//局部(静态)初始化区
int x2 = 0; //局部变量,栈区
}
int main()
{
int x; //局部变量,存放在栈区
char s[]="abc"; //局部变量,栈
char *p1; //局部变量,栈
char *p2="abcdef"; //"abcdef/0"在常量区,p3在栈上。
p1 = (char *)malloc(23); //分配得来得23和25字节的区域就在堆区
strcpy(p1,"abcdef"); //"abcdef\0"放在常量区,编译器可能会将它与p2所指向的"abcdef" 优化成一个地方
int* p4 = new int(2); //p4是局部变量,存放在栈区,p4指向的内存是堆区
}
分配方式
C/C++内存分配方式有3种:
1.从静态存储区域分配: 是在程序编译和链接时就确定好的内存;这些内存在程序整个运行期间都存在,如全局变量,static变量等。
2.在栈上创建:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限,如数组,局部变量,形参。
3.从堆上分配:亦称动态内存分配 ,是在程序加载、调入、执行的时候分配/回收的内存;程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定 ,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
实现
1.new和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; }
};
2.利用空间配置器alloc
在STL中空间配置器的4个函数:
内存的配置:alloc::allocate();
对象的构造:alloc::construct();
对象的析构:alloc::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);
}
};
};
STL中容器默认空间分配器为std::allocator<_Tp>,内存分配和释放的接口allocate和deallocate内部实现只是将::operator new和::operator delete进行封装,没用做特殊处理;如果还想深入了解STL的空间配置器的相关内容,可以查阅我的博客深入理解STL空间分配器(一)-CSDN博客、深入理解STL空间分配器(二)-CSDN博客、深入理解STL空间分配器(三)-CSDN博客、深入理解STL空间分配器(四)-CSDN博客。
3.用malloc和free
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);
}
};
4.静态内存分配
代码如下:
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博客