【C++基础九】内存管理(new和delete)

在C语言中,有四个内存管理函数:malloc,calloc,realloc和free
但是它们的使用十分的不方便
例:
int* p = (int*)malloc(sizeof(int) * n);
malloc函数不会初始化变量,如果变量是自定义类型,C语言的内存管理函数无法自动调用变量的构造和析构函数

1.new

申请单个元素的空间,使用new操作符,申请连续的空间,使用new[]

1.1new的使用

  1. 开辟多个空间
int* p = new int[100];

开辟100个int类型的空间,将空间的地址赋值给p指针变量

  1. 开辟一个空间并初始化
int main()
{
    
    
	int* p = new int(10);
	return 0;
}

申请一个int类型的空间并初始化值为10

  1. 申请连续的空间并初始化
int main()
{
    
    
    int* p = new int[10] {
    
    1, 2, 3, 4};//申请10个int的空间并初始化
    return 0;
}

申请10个int类型的空间并初始化了前四个空间的值,替他空间

1.2new自定义类型对比malloc

new自定义类型用法与内置类型相同

  1. 在申请自定义类型的空间时,new会调用构造函数,malloc不会
  2. new可以初始化变量的内容
class A
{
    
    
public:
    A(int a = 0)
        : _a(a)
    {
    
    
        cout << "A():" << this << endl;
    }
    ~A()
    {
    
    
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};

int main()
{
    
    
    A* p = (A*)malloc(sizeof(A) * 10);
    free(p);
    A* p1 = new A[2];
    delete[]p1;
    return 0;
}

2.delete

2.1delete的使用

  1. 直接使用delete
int* p = new int(10);
//使用指针p
delete p;
  1. 使用delete[]
int* p = new int[10]{
    
    1,2,3,4};
//使用指针p
delete[] p;

当new使用方括号[]开辟多个空间时,delete也要对应的加上[]来释放空间,否则会报错

2.2delete自定义类型对比free

  1. delete会调用自定义类型的析构函数,而free不会
  2. delete面对不同的情况需要加上[ ],然而free不用考虑
class stack
{
    
    
public:
    stack()//构造
    {
    
    
        cout << "stack()" << endl;
        _a = new int[4];
        _top = 0;
        _capacity = 4;
    }
    ~stack()//析构
    {
    
    
        cout << "~stack()" << endl;
        delete[] _a;    
        _top = _capacity = 0;
    }
private:
    int* _a;
    int _top;
    int _capacity;
};

int main()
{
    
    
    stack p;
    stack*p1 = new stack;
    delete p1;
    return 0;
}

类的实例化对象生成p,在栈上,调用构造函数,在堆上开辟了4个int类型的数组
p1是一个指针,在栈上指向在堆上申请的一个stack, 再调用构造函数,_a=newstack[4],_a再次指向在堆上申请的4个int类型的数组
若将delete p1改为 free(p1),会少调用析构函数,直接释放stack空间
导致无法释放堆上申请的4个int类型的数组,从而导致内存泄露

3.全局函数operator new

operator new是全局函数,不是运算符重载

库中的operator new函数:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
    
    
void *p;
while ((p = malloc(size)) == 0)
	if (_callnewh(size) == 0)
	{
    
    
	    // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
	    static const std::bad_alloc nomem;
	    _RAISE(nomem);
	}
return (p);
}

new的底层实际上是使用了malloc来实现开辟空间
开辟空间成功,会返回指向此空间的指针
开辟空间失败,会抛异常

4.全局函数operator delete

库中的operator delete函数:

#define free(p) _free_dbg(p, _NORMAL_BLOCK)
void operator delete(void *pUserData)
{
    
    
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);
     __TRY
         pHead = pHdr(pUserData);
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);
     __END_TRY_FINALLY
     return;
}

delete里面调用了:_free_dbg(p, _NORMAL_BLOCK),此函数正是free函数定义的宏替换,说明delete的底层实现实际上是调用了C语言中的free函数,delete是使用free来释放空间的

5.new和delete的实现原理

new的原理步骤:

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数完成对象的构造

new T[N]的原理步骤:

  1. 调用operator new[]函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数

delete的原理步骤:

  1. 在空间上执行析构函数完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间

delete[]的原理步骤:

  1. 在释放的对象空间上执行N次析构函数完成N个对象中资源的清理
  2. 调用operator delete[]释放空间