C/C++内存分布
- 代码段:可执行的代码、只读常量
- 全局数据区:存储全局数据和静态数据(static修饰)
- 堆区:使用malloc、calloc、realloc等申请的空间
- 栈区:非静态局部变量、函数参数、返回值等等
C/C++语言中动态内存管理方式
C中使用malloc/calloc/realloc申请堆上的空间,和栈申请空间不同的是,堆上的空间需要编程人员自己调用函数free()释放。
malloc和free
malloc用来动态内存的开辟:
void* malloc(size_t size)
- 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针;
- 如果开辟成功,则返回一个指向开辟好空间的指针;
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查;
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
free函数用来释放动态开辟的内存:
void free(void *ptr)
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的;
- 如果参数 ptr 是NULL指针,则函数什么事都不做。
calloc
void* calloc(size_t num,size_t size)
- 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0;
- 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
realloc
void *realloc(void *ptr,size_t szie)
- ptr 是要调整的内存地址,size 调整之后新大小;
- 返回值为调整之后的内存起始位置;
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
malloc、calloc、realloc三者的区别
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据;realloc也不会对所申请的内存空间进行初始化,但它让内存动态空间管理更加灵活。三者都申请空间成功返回空间的首地址,否则返回NULL。
在C++ 中单个元素的空间使用new来申请,delete来释放;多个元素的空间使用new[ ]来申请,delete[ ]来释放;不能交叉使用。
这里需要注意,new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operatornew全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数,完成对象的构造
delete的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[ ]函数,在operator new[ ]中实际调用operator new函数完成N个对象空间的申
请
2. 在申请的空间上执行N次构造函数
delete[ ]的原理
1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2. 调用operator delete[ ]释放空间,实际在operator delete[]中调用operator delete来释放空间
定位new表达式(placement-new)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
- new (place_address) type或者new (place_address) type(initializer-list)
- place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
malloc/free和new/delete的区别
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
- new/delete比malloc和free的效率稍微低点,因为new/delete的底层封装了malloc/free
代码说明
#include<iostream>
#include<stdlib.h>
using namespace std;
//内存空间管理
class Test
{
public:
Test(int data=1)
: _data(data)
{
cout << "Test():" << this << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
void Test1()
{
// 申请单个Test类型的空间
Test* p1 = new Test;
delete p1;
//申请多个Test类型的空间
Test *p2 = new Test[10];
//delete[]所释放的字节数为10*sizeof(test)+1,表示申请空间的大小+用于存放申请元素个数所占用的一个字节
delete[]p2;
}
void Test2()
{
//p现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
Test *p = (Test*)malloc(sizeof(Test));
//定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义
//类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
new(p) Test();
}
/*
*让一个类只能在堆上创建对象;
*创建对象有两个途径:1.调用构造函数 2.调用拷贝构造函数
*封锁这两个途径
*/
class Test3 {
public:
//提供在堆上创建对象的函数
static Test3 *HeapCreate()
{
return new Test3;
}
private:
//声名构造函数和拷贝构造函数为私有(C++98)
Test3() {};
Test3(const Test3&) {};
//C++11
/*Test3(const Test3&)=delete;*/
};
/*
*让一个类只能在栈上创建对象;
*在堆上创建对象的途径:1.new 2.new[] 但new[]是对new多次使用,所以只需封锁new
*/
class Test4{
public:
Test4()
{}
private:
void* operator new(size_t size){}//一般封锁了new,同时也封锁delete
void operator delete(void *ptr){}
};
int main()
{
//Test1();
//Test2();
//Test3 *p = Test3::HeapCreate();
//Test4 *p=new Test4;
system("pause");
return 0;
}