目录
(3) operator new与operator delete函数
1. C/C++内存分布
栈区:存储局部变量,函数参数,函数返回值,栈是向下增长的(压栈)。
内存映射段:高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口,创建共享共享内存,做进程间通信。
堆区:程序运行时的动态内存分配,堆可以向上增长。
静态区(数据段):存储静态变量,全局变量。
常量区(代码段):存储只读常量(字符串常量),可执行代码(二进制指令)。
2. C语言中动态内存管理方式
malloc,calloc,realloc的区别:malloc是分配堆区上的空间,分配的空间的值不确定,而calloc则是分配num个元素的空间,每个元素size字节,并将每个元素初始化为0,两个函数区别就是是否初始化空间中值为0。realloc常用于扩容,在原来的空间上开辟更大的空间,或者找一块更大的空间,将原有数据拷贝,并释放原空间,前者方法是在原来空间后有足够大的空间时使用的。三个函数在空间开辟失败时都会返回NULL。
malloc原理:
1)当开辟的空间小于 128K 时,调用 brk()函数,移动指针 _enddata(_enddata 指的是 Linux 地址空间中堆段的末尾地址)
2)当开辟的空间大于 128K 时,系统调用mmap()函数来在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。
如图:
1)brk 是将数据段(.data)的最高地址指针 _enddata 往高地址推
2)mmap 是在进程的虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空闲的虚拟内存。
这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
3. C++中动态内存管理
(1)new/delete操作内置类型
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
void test(){
int* p1 = new int;//动态内存申请一个int类型大小的空间
int* p2 = new int(10);//动态内存申请一个int类型大小的空间,并初始化为10
int* p3 = new int[10];//动态内存申请一个10个int类型大小的空间
int* p4 = new int[10]{20,30}; //动态内存申请一个10个int类型大小的空间,
//并将前两个元素初始化为20,30
cout << *p1 << endl;
cout << *p2 << endl;
cout << *p3 << endl;
cout << *p4++ << endl;
cout << *p4-- << endl;
delete p1;
delete p2;
delete[]p3;//delete[]要和new[]搭配使用
delete[]p4;//delete[]要和new[]搭配使用
return;//new[]是申请连续的存储空间,delete[]是释放连续的存储空间
}
int main()
{
test();
return 0;
}
(2)new/delete操作自定义类型
在用new和delete操作内置类型时,和用malloc和free没什么区别,但在操作自定义类型时就不一样了。因为new不仅会开辟空间,还会调用自定义类型的默认构造函数,delete则会调用其析构函数并释放空间,而malloc和free只有开辟空间和释放空间。
例如:
class A {
public:
A(int m=200):_m(m) { cout << "A() is called!" << endl; }
~A() { cout << "~A() is called!" << endl; }
void print() { cout << "m==" << _m << endl; }
int _m = 100;
};
void test()
{
A* a1 = (A*)malloc(sizeof(A));
a1->print();
cout << "--------------" << endl;
A* a2 = new A;
a2->print();
delete a2;
cout << "--------------" << endl;
free(a1);
return;
}
int main()
{
test();
return 0;
}
输入如下:
(3) operator new与operator delete函数
函数原型:(还有另外4个不抛出bad_alloc异常的版本)
void* operator new(size_t);
void* operator new[](size_t);
void* operator delete(void*);
void* operator delete[](void*);
new和malloc,delete和free是类似的。但new和delete是申请和释放单个元素的空间,new[]和delete[]是申请和释放连续的一段空间。new开辟空间失败时抛异常,malloc则返回NULL。
new:调用operator new开辟空间,再调用构造函数初始化对象。delete:调用析构函数清理对象的资源,再调用operator delete释放空间。new T[N]:调用operator new[]函数开辟空间,operator new[]实际上是调用了operator new申请N个对象的空间,再调用N次构造函数初始化N个对象。delete[]: 调用N次析构函数,再调用 operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间。
(4) 定位new表达式(placement-new)
operator new和operator delete是标准库的全局函数,可以直接调用它们来开辟/释放空间,这时operator new开辟的空间,还没调用构造函数初始化对象,应该使用定位new表达式来构造对象。定位new一般配合内存池使用,因为内存池分配的空间没有初始化,需要用定位new表达式,显示调用构造函数来进行初始化。
格式:
new (place_address) typenew (place_address) type(initializer-list)
class A {
public:
int m_a;
A(int a=0):m_a(a) { cout << "call A(int a=0)" << endl; }
~A() { cout << "call ~A()" << endl; }
};
int main() {
A* p = (A*)operator new(sizeof(A));
new (p)A{ 100 };
cout<<p->m_a<<endl;
p->~A();//用对象的指针或引用显示调用析构函数
operator delete (p);
return 0;
}
输出:
(5)malloc、free和new、delete的区别
定位上:
malloc和free是函数,new和delete是操作符
使用上:
malloc申请的空间不会初始化,new则可以
malloc返回的指针是void*需要强制类型转换,new则自动根据类型匹配,不用强制类型转换
malloc需要手动计算空间大小去开辟,new则是自动计算类型大小
malloc是开辟空间,开辟失败返回NULL,需要对指针判空操作,new则是开辟空间并调用构造函数,开辟失败时抛异常,需要捕获异常
free是释放空间,delete是调用析构函数并释放空间
原理上:
new本质是调用operator new开辟空间,而operator new本质上也是调用malloc开辟空间,delete也同理,本质上也是调用free函数。
operator new和operator delete的源码:
void* __CRTDECL operator new(size_t const size)
{
for (;;)
{
if (void* const block = malloc(size))
{
return block;
}
if (_callnewh(size) == 0)
{
if (size == SIZE_MAX)
{
__scrt_throw_std_bad_array_new_length();
}
else
{
__scrt_throw_std_bad_alloc();//申请空间失败,抛异常
}
}
}
}
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK);
#else
free(block);
#endif
}