C++ 内存管理
C++内存分配
一般来说C++ 操作内存用的是 new/ delete
实际最后最用调用的是malloc /free
调用层次如下
![转自侯捷老师ppt](https://img-blog.csdnimg.cn/17390fe79cb24acb8878e1c78db5b1ca.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjE5MzcwNA==,size_16,color_FFFFFF,t_70)
最后malloc 和free 就会调用来自操作系统的API 来实现真正的分配内存,当然我们也可以直接调用操作系统的api来分配内存使用,只是一般情况下我们不会这么操作。
C++ 使用new 分配的时候做了什么
new 实际上应该被称为 operator new( ) 除此之外还有一个::operator new() 表示全局的new 当然与之对弈的 也有operator delete, ::operator delete 这两个函数也是允许被重载的,本文说的的内存管理基本上是基于对几个函数的操作。
回到主题 我们在使用new 的时候编译器会帮我们做哪些额外的操作呢?
class A
{
public :
A()
{
}
} ;
int main(void)
{
A * a = new A();
delete a;
return 0;
}
比如这个代码
编译器在看到new 只会就会将代码扩充成这个样子
class A
{
public :
A()
{
}
} ;
int main(void)
{
// A * a = new A();
//上面这句就会被扩展成这个样子
A * a;
try{
void *mem = operator new (sizoef(A) );
a = static_cast<A *>(mem);
a->A::A();
//直接调用构造函数是不被允许的,只有编译器扩展的才可以调用
}
catch(std::bad_alloc)
{
}
//new end
//delete a;
//delete 扩展
a->~A();//可以手动调用
operator delete(a);
//delete end
return 0;
}
这样我们就可以很清晰的知道 如果重载了 new /delete 是在什么时候被调用的 同时我们也知道了 程序员是不能手动调用构造函数的,如果要调用要怎么操作呢,C++提供了一种原地创建对象的方式(在指定地址创建对象)new §A(); 在p 所指向的位置创建这个对象,这个在后面也有详细的说明。
了解了这个我们在了解一下 new 一个数组的时候是怎么操作的 以及为什么new 数组为什么要使用delete[]
分配一个数组
#include <iostream>
using namespace std;
class A
{
public :
A()
{
}
} ;
int main(void)
{
A *a = new A[3];
delete [] a ;
return 0;
}
上面代码创建 了一个大小为3 的数组 然后调用delete 释放了这个空间
这里其实有一个一直没有解释的问题, 为什么创建的时候指定了个数,但是释放的时候没有指定要释放的个数呢?这里就涉及到了C++ 分配一个数组进行的一个操作。
a 指向的实际上是cookice 的位置 在delete 的时候因为Cookice 中有记录这样就可以成功的调用这里所有的析构函数,如果没有 调用delete [] 调用的是 delete 会发生什么呢,很显然就会没有调用下面对象的析构函数 这样会内存泄露吗 实际上这里new 的空间是放回去了, 但是如果 数组中析构函数有delete 就不会被调用到 所以还是存在内存泄露的风险。
cookice 的位置有可能在上面有可能在下面这取决于平台实现 对于程序员来说其实并不需要关心
#include <iostream>
using namespace std;
class A
{
public :
A()
{
}
A(int _id )
:id(_id)
{
cout<<"this "<<this<<" id = "<<id<<endl;
}
~A()
{
cout<<"~ this "<<this<<" id = "<<id<<endl;
}
private :
int id;
} ;
int main(void)
{
A *a = new A[3];
A * temp = a;
for(int i = 0;i<3;i++)
{
new(temp ++ )A(i);
// 这里我们就是用上面说的在指定空间中创建对象
}
delete [] a ;
return 0;
}
使用这个我们可以测试释放方式
就此我们就知道了C++ 在new 实际上干了什么 ,实际上operator new /delete 内部还是调用malloc()接下来我们就要尝试 重载new、delete来实现一些操作
重载operator new / operator delete
根据上面的说法 每次在使用new 的时候都会展开使用operator new C++ 是允许这个的重载
通过上面的展开代码我们也知道了重载以后具体调用在哪里