回顾C语言内存管理
C语言对内存申请的方式有:malloc,calloc,realloc。那么这三个之间的区别是什么?
malloc只负责申请空间,不初始化。calloc申请空间同时初始化。realloc是对已有空间的扩容,有可能原地扩容,也有可能异地扩容。
- 如果是原地扩容,那么
realloc的返回值应当和calloc的返回值一样。- 如果是异地扩容,系统会自动释放
calloc的返回值,并且给realloc重新返回值。
C++内存管理
C++兼容C语言,因此满足C语言对内存的管理。但是自己也有一套管理内存的方式:
new和delete
两者对比
- 对内置类型
int main()
{
// 申请一个int型空间
// C
int* p1 = (int*)malloc(sizeof(int));
// C++
int* pp1 = new int;
// 申请10个int型空间
// C
int* p2 = (int*)malloc(sizeof(int) * 10);
//C++
int* pp2 = new int[10];
return 0;
}

可以看到,new和malloc对内置类型都是一样的,只申请空间,但不初始化。
int main()
{
// 申请一个int
int* p1 = new int;
int* p2 = (int*)malloc(sizeof(int));
// 申请一个int并初始化为1
int* p3 = new int(1);
// 申请10个int
int* p4 = new int[10];
// 申请10个int并初始化
int* p5 = new int[10]{ 1,2,3,4,5 };
return 0;
}

- 对自定义类型
class A
{
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
int main()
{
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
return 0;
}





可以看到:
- 操作符
new会调用构造函数,delete会调用析构函数。new会申请空间并初始化,而malloc仅仅是开空间。
尤其在如下场景:
struct ListNode
{
int _val;
ListNode* _next;
ListNode(int val)
:_val(val)
,_next(nullptr)
{}
};
int main()
{
ListNode* n0 = (ListNode*)malloc(sizeof(ListNode));
ListNode* n1 = new ListNode(1);
ListNode* n2 = new ListNode(2);
ListNode* n3 = new ListNode(3);
return 0;
}

可以看到,对自定义类型而言,使用new比使用malloc更加简单方便。
new和delete的注意事项
- 对自定义类型
new多次和delete多次
class A
{
public:
A()
:_a(0)
{
cout << this << " A()" << endl;
}
~A()
{
cout << this << " ~A()" << endl;
}
private:
int _a;
};
int main()
{
// 先调用operator new --> 封装malloc
// 然后调用构造函数
//A* p = new A;
// 先调用析构函数,在释放资源
// operator delete -> free
//delete p;
// 调用operator new[] --> operator new --> 封装malloc
// 调用5次构造函数
A* p1 = new A[5];
cout << "p1的地址: " << p1 << endl;
// 释放5次析构函数
delete[] p1;
return 0;
}

==new几个就调用几次构造,delete也是一样。==
free和new搭配使用
-
情况一正常
少调用析构函数,没问题。
class A
{
public:
A()
:_a(0)
{
cout << this << ": A()" << endl;
}
~A()
{
cout << this << ": ~A()" << endl;
}
private:
int _a;
};
int main()
{
// new内置类型的时候,没有构造函数,也没有析构函数。
int* p1 = new int[5];
// 调operator delete内部也是调free
// 这里调用operator delete的时候没有析构函数和直接掉free没区别
free(p1);
A* p2 = new A;
// 这里调free只是没有调用析构函数,但是A类中也没有资源要释放。也不影响
free(p2); // 少调用了析构函数
return 0;
}

-
情况二不正常
少调用析构函数,导致内存泄漏,但是不会报错。。
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()
{
// st1会调用构造函数,st2会调用吗?
// 不会,因为内置类型不会调用构造函数,自定义类型才会
Stack st1;
Stack* st2 = new Stack;
delete st2;
//free(st2);
return 0;
}



所以,自定义类型中如果有资源释放,用free可能会造成内存泄漏。。因此,最好不要混合使用