C++中内存分配的几种方式
目录
写在前面
C++中内存分配十分重要,并且容易造成内存泄露等等问题,之前只是知道new和delete底层调用的是malloc和free,下面就深入分析一下new和delete的执行步骤以及自定义内存池
C++中内存分配的层次: 其中 C++ Library分配器就是STL中的内存分配结构,将new/malloc以及delete/free进一步封装,减少new/malloc的次数,做成一个内存池
函数解释
malloc/free函数
C语言中的内存分配函数,速度很快,返回的是void* 类型的指针
void* p1 = malloc(512);
free (p1);
new/delete函数
new动作包含两个动作:
- 开辟空间
- 构造对象
delete动作包含两个动作:
- 析构对象
- 释放空间
complex<int>* p2 = new complex<int>;
delete p2;
array new/delete函数
如果使用array new,则必须使用array delete相对应,否则会出现内存泄露的现象
Complex *pca = new Complex[3]; //唤起三次ctor
delete [] pca; //唤起三次dtor
::operator new/delete函数
这是一个全局函数,在调用new函数的时候,首先编译器会先调用这个函数,并且此函数是唯一可以重载,使用自己定义的::operator new函数(但是一般不会重载全局的::operator new/delete函数),而是重载类中对应的operator new/delete函数,这样就不会影响全局的::operator new/delete 函数
::operator new返回的也是void* 类型的指针
void* a = ::operator new (sizeof(int));
::operator delete(a);
new整体流程
Complex *pc = new Complex(1,2);
上述程序会被编译器转换为:
Complex *pc;
try{
void *mem = operator new (sizeof(Complex)); //分配空间
pc = static_cast<Complex*>(mem); //类型强制转换
pc->Complex::Complex(1,2); //调用构造函数
//只有编译器可以像上面一样直接调用构造函数(ctor),我们可以使用placement new来调用
}
catch(std::bad_alloc){
//分配失败
}
我们知道对象的指针是不可以直接调用构造函数,但是编译器可以直接调用,此为编译器的特权
首先new会调用operator new函数,如果我们不重载operator new函数的话,operator new函数中调用的即为malloc函数,然后进行强制类型转换,以及对象的构造。
delete整体流程
delete pc;
上述程序会被编译器转换为:
pc->~Complex(); //先析构
operator delete(pc); //然后释放内存
//没有重载的operate delete函数
void _cdecl operator delete (void *p) //operator delete里面调用free函数
{
free(p);
}
没有重载的全局operator delete函数直接调用free函数
operator new/delete的重载
#include <iostream>
#include <initializer_list>
#include <algorithm>
#include <thread>
#include <cmath>
#include <vector>
#include <tuple>
#include <utility>
#include <type_traits>
using namespace std;
class Foo
{
public:
int _id;
int _data;
string _str;
public:
Foo():_id(0) { cout << "default ctor.this="<<this<<" id="<<_id<<endl; }
Foo(int i):_id(i){ cout << "default ctor.this="<<this<<" id="<<_id<<endl; }
~Foo() {cout <<"dctor.this="<<this<<" id="<<_id<<endl; }
//在类中进行重写 这里要使用静态函数,因为这时对象还是不存在的,没有构造出来
static void* operator new(size_t size);
static void operator delete(void *pdead,size_t size);
static void* operator new[](size_t size);
static void operator delete[](void *pdead,size_t size);
void *operator new(size_t size,void *start)
{
return start;
}
};
void* Foo::operator new(size_t size)
{
Foo *p = (Foo *)malloc(size);
cout<<"operator new"<<endl;
return p;
}
void Foo::operator delete(void* pdead,size_t size)
{
cout << "operator delete"<<endl;
free(pdead);
}
void* Foo::operator new[](size_t size)
{
Foo *p = (Foo *)malloc(size);
cout<<"operator new[]"<<endl;
return p;
}
void Foo::operator delete[](void* pdead,size_t size)
{
cout << "operator delete[]"<<endl;
free(pdead);
}
int main()
{
cout << sizeof(Foo)<<endl;
Foo *p = new Foo(7);
delete p;
Foo *pArray = new Foo[5];
delete [] pArray;
// //这样会绕过上面的函数,使用全局的new、delete函数
// Foo* p = ::new Foo(7);
// ::delete p;
//
// Foo* pArray = ::new Foo[5];
// ::delete [] pArray;
}
我们以同样的方式可以重载全局的operator new/delete函数