知识储备:
内存泄漏:通俗的来说内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。
常见的几种内存泄漏:
void MemoryLeaks()
{
// 1、内存申请了忘记释放
int *pTest = (int *)malloc(10*sizeof( int));
assert(NULL != pTest);
DoSomething();
// 2、程序逻辑错误,以为释放了,实际内存泄露
int *pTest1 = (int *)malloc(10*sizeof( int));
int *pTest2 = (int *)malloc(10*sizeof( int));
DoSomething();
pTest1 = pTest2;
free(pTest1);
free(pTest2);
// 3、程序访问越界操作,将堆破坏
char *pTest3 = (char *)malloc(5);
strcpy(pTest3, "Memory Leaks!");
free(pTest3);
// 4、释放时传入的地址和申请时的地方不相同
int *pTest4 = (int *)malloc(10*sizeof( int));
assert(NULL != pTest4);
pTest4[0] = 0;
pTest4++;
DoSomething();
free(pTest4);
}
new/delete:
1.C++中的操作符
2.不需要头文件支持。
malloc/free:
1.是函数。
2.需要头文件支持。<malloc.h>
new和malloc:
new做两件事,一是分配内存,二是调用类的构造函数
new建立的是一个对象,而malloc分配的是一块内存;new建立的对象可以用成员函数访问,不要直接访问它的地址空间;malloc分配的是一块内存区域,用指针访问,可以在里面移动指针;new出来的指针是带有类型信息的,而malloc返回的是void指针。
delete和free:
delete先调析构函数,再释放空间
free释放一块指定的内存空间
new :
1. 调用operator new分配空间。
2. 调用构造函数初始化对象。
delete:
1. 调用析构函数清理对象
2. 调用operator delete释放空间
new[]:
1. 调用operator new分配空间。
2. 调用N次构造函数分别初始化每个对象
delete[]:
1. 调用N次析构函数清理对象。
2. 调用operator delete释放空间。
new/delete 和 malloc/free的区别和联系:
1. 它们都是动态管理内存的入口。
2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3. malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)。
4. malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
在调用new和delete时可以发现,在底层new实际调用的是operator new,delete实际调用的是operator delete。
说在前面:
标准库函数operator new 和operator delete的命名容易让人误解,与其他operator函数不同,这些函数没有重载new或者delete表达式,实际上,我们不能重定义new和delete表达式的行为。
void * operator new (size_tsize);
void operator delete (size_tsize);
void * operator new [](size_tsize);
void operator delete[] (size_tsize);
new --> operator new (进一步调用malloc申请空间) + 构造函数
delete ->析构函数 + operator delete (调用free)
operator new()->失败抛出异常
malloc ->失败返回错误码
由此我们可以推出new在调用内存真正做了什么:
总结:
1. operator new/operator delete operator new[]/operator delete[] 和malloc/free用法一样。
2. 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
3. 实际operator new和operator delete只是malloc和free的一层封装
new[count]:
一般情况下,会开辟出4字节来存放count,但是编译器在此处会进行优化。有时候也不会开辟空间。如析构函数可调可不调时(自己写的构造函数一定要调),或不需要清理资源时,编译器优化为不需要调用析构函数,由此不会开辟内存空间
动态内存管理:
没有调析构和构造, 指针的偏移位置不正确
可能崩溃:
new--delete[] delet时指针往前偏移了4字节(误认为)
new[]-- free free没往前偏移4字节
可能会内存泄漏:
new--free
#include <iostream>
using namespace std;
class AA
{
public:
AA(size_t size = 10)
:_size(size)
,_a(0)
{
cout << "AA()" << endl;
if (_size > 0)
_a = new int[_size];
}
~AA()
{
cout << "~AA()" << endl;
if (NULL != _a)
{
delete[]_a;
_a = NULL;
_size = 0;
}
}
private:
int *_a;
size_t _size;
};
int get_size()
{
return 10;
}
int main()
{
int *p0 = new int[get_size()]; //括号内大小必须是整形,但不必是常量
//内置类型也有构造函数,但是不能被重写
int p1 = 6; //初始化为10
p1 = int(); //构造函数,初始化为0
p1 = int(20); //初始化20
AA* p2 = new AA;
delete p2; //匹配: 一次构造,一次析构
delete[]p2; //不匹配:崩溃 越界释放
free(p2); //不匹配:可能会内存泄露
AA* p3 = new AA[10];
delete p3; //不匹配:10次构造,一次析构,内存释放位置错误
delete[]p3; //匹配:
free(p3); //不匹配:10构造,0析构,内存释放位置错误
int* p4 = new int[10];
delete p4; //正确,不调析构,无内存需清理,指针不会发生偏移
delete[]p4; //匹配
free(p4); //不崩溃,但是不代表正确使用
AA* p5 = new AA[]; //
delete p5; //崩溃
delete[]p5; //匹配
free(p5); //崩溃
int* p4 = new int;
int* p5 = new int(3);
int* p6 = new int[3];
int* p7 = (int*)malloc(sizeof (int));
delete[] p4; //不崩溃,
delete p5; //不崩溃
free(p5); //no
delete p6; //no
delete p7; //no
int *p = new int[10]{0};
system("pause");
return 0;
}
new和delete、new[]和delete[]一定匹配使用,一定匹配使用,一定匹配使用!!!
C库malloc/free等来动态管理内存,为什么C++还要定义new/delete运算符来动态管理内存呢?
malloc 与free 是C++/C 语言的标准库函数,new/delete 是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数, 对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。