C++中内存分配的几种方式

目录

写在前面

函数解释

malloc/free函数

new/delete函数

array new/delete函数

::operator new/delete函数

new整体流程

delete整体流程

operator new/delete的重载


写在前面

C++中内存分配十分重要,并且容易造成内存泄露等等问题,之前只是知道new和delete底层调用的是malloc和free,下面就深入分析一下new和delete的执行步骤以及自定义内存池

C++中内存分配的层次: 其中 C++ Library分配器就是STL中的内存分配结构,将new/malloc以及delete/free进一步封装,减少new/malloc的次数,做成一个内存池

C++中内存分配的几种方式

函数解释

malloc/free函数

C语言中的内存分配函数,速度很快,返回的是void* 类型的指针

void* p1 = malloc(512);
free (p1);

new/delete函数

new动作包含两个动作:

  1.  开辟空间
  2. 构造对象

delete动作包含两个动作:

  1. 析构对象
  2. 释放空间
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函数

参考视频

https://www.bilibili.com/video/av24603588