C++程序的内存分为两部分:
- 栈:用来存储内部申明的量,也就是局部变量。
- 堆:这是程序中未使用的内存,在程序运行的时候动态的分配内存。
new和delete运算符:new运算符是为所制定的变量分配堆内的内存,然后返回内存的地址。delete是把我们分配的内存释放。
1 double *pv = NULL; //定义一个指针类型的变量2 pv = new double; //new运算符分配double类型的内存,把地址给pv3 if(!pv) //检查指针是否为空4 {5 cout << "Error: out of memory." <
malloc() 函数在 C 语言中就出现了,C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
数组的动态分配内存:
- 一维数组
int *pv = NULL;pv = new char[100];delete [] pv; //或者delete pv;
- 二维数组
1 int **pv = NULL; //定义一个空指针 2 pv = new char*[100] //给一个指针数组分配内存,并把内存的地址给pv 3 for(int i=0;i>100,i++) 4 { 5 pv[i] = new char[10];//给每一个数组分配内存,并把地址给存到对应的每个指针数组 6 } 7 8 for(int i=0;i>100,i++) 9 {10 delete [] pv[i]; //释放每个指针数组所指向的数据11 }12 delete [] pv; //释放指针数组的内存
- 三维数组
1 int ***pv = new int **[m]; //把指针数组的地址存到pv中 2 for(int i =0;i>m;i++) 3 { 4 Array[i] = new *[n]; // 再向指针数组中的每个元素分别存入一个指针数组 5 for(int j = 0;j>n;j++) 6 { 7 Array[i][j] = new int [q]; //最后向上面指针数组中的每个元素所存的指针数组中的每个元素存一个数组的地址。 8 } 9 }10 11 //释放12 for(int i=0;i>m;i++)13 {14 delete [] Array[i];//然后再释放我们存数组的指针数组。15 for(int j=0;j>n;j++)16 {17 delete [] Array[i][j]; //先释放我们最后分配的内存,也就是存每个数组地址的指针18 }19 }20 21 delete [] pv; //最后释放我们的只存一个地址的pv。
数组的分配内存感觉有点不好理解,配上图来理解会比较容易一点。理解了三维的其他的就很容易了。
对象的动态分配内存:它其他的没有不同只是数据类型变了一下,其他的是一样的。
1 #include2 #include 3 using namespace std; 4 class student 5 { 6 public: 7 student(){cout << "这是构造函数。" << endl;} 8 ~student(){cout << "这是析构函数。" << endl;} 9 };10 11 int main()12 {13 student *pvplus = new student[4];14 delete [] pvplus;15 return 0;16 }
结果为:
这是构造函数。这是构造函数。这是构造函数。这是构造函数。这是析构函数。这是析构函数。这是析构函数。这是析构函数。
发现当对象被分配内存时,如果我们给对象数组分配内存,那么它会给数组中的每个元素不仅分配了内存还创建对象。
delete和delete[]:
1、针对简单类型 使用 new 分配后的不管是数组还是非数组形式内存空间用两种方式均可,此种情况中的释放效果相同,原因在于:分配简单类型内存时,内存大小已经确定,系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数, 它直接通过指针可以获取实际分配的内存空间,哪怕是一个数组内存空间(在分配过程中 系统会记录分配内存的大小等信息,此信息保存在结构体_CrtMemBlockHeader中)
2、针对类Class,两种方式体现出具体差异
当你通过下列方式分配一个类对象数组:
1 class A 2 { 3 private: 4 char *m_cBuffer; 5 int m_nLen; 6 public: 7 A(){ m_cBuffer = new char[m_nLen]; } 8 ~A() { delete [] m_cBuffer; } 9 };10 A *a = new A[10];11 12 // 仅释放了a指针指向的全部内存空间 但是只调用了a[0]对象的析构函数 剩下的从a[1]到a[9]这9个用户自行分配的m_cBuffer对应内存空间将不能释放 从而造成内存泄漏13 delete a;14 15 // 调用使用类对象的析构函数释放用户自己分配内存空间并且 释放了a指针指向的全部内存空间16 delete [] a;
所以总结下就是,如果ptr代表一个用new申请的内存返回的内存空间地址,即所谓的指针,那么:
- delete ptr -- 代表用来释放内存,且只用来释放ptr指向的内存。
- delete[] rg -- 用来释放rg指向的内存,!!还逐一调用数组中每个对象的 destructor!!
对于像 int/char/long/int*/struct 等等简单数据类型,由于对象没有 destructor,所以用 delete 和 delete [] 是一样的!但是如果是C++ 对象数组就不同了!
最后我们看下new 和 malloc 内部的实现方式有什么区别?
new 的功能是在堆区新建一个对象,并返回该对象的指针。所谓的【新建对象】的意思就是,将调用该类的构造函数,因为如果不构造的话,就不能称之为一个对象。而 malloc 只是机械的分配一块内存,如果用mallco 在堆区创建一个对象的话,是不会调用构造函数的。严格说来用 malloc 不能算是新建了一个对象,只能说是分配了一块与该类对象匹配的内存而已,然后强行把它解释为【这是一个对象】,按这个逻辑来,也不存在构造函数什么事。同样的,用 delete 去释放一个堆区的对象,会调用该对象的析构函数。用 free 去释放一个堆区的对象,不会调用该对象的析构函数。
new 一个对象,用free去释放,会有什么问题:
1 #include2 using namespace std; 3 class nf { 4 public: 5 nf(); 6 ~nf(); 7 public: 8 int * get(); 9 void set(int i);10 private:11 int * pi;12 };13 14 nf::nf(){ cout << "nf construction" << endl; pi = new int(0);}15 nf::~nf(){ cout << "nf destruction" << endl; delete pi;}16 int * nf::get(){ return pi;}17 void nf::set(int i){ *pi = i;}18 19 int main()20 {21 nf* pnf = new nf();22 pnf->set(100);23 int *pp = pnf->get();24 cout << *pp << endl;25 //delete pnf;26 free(pnf);27 cout << *pp << endl;28 system("pause");29 return 0;30 }
结果为:
nf construction100100
我们看到并没有去执行析构函数,存在内存泄漏。
1 #include2 #include 3 using namespace std; 4 class student 5 { 6 public: 7 student() { cout << "这是构造函数。" << endl; } 8 ~student() { cout << "这是析构函数。" << endl; } 9 };10 11 int main()12 {13 student *pvplus = (student *)malloc(sizeof(student));14 free(pvplus);15 return 0;16 }
构造函数和析构函数都没有被执行。
对于我这种还没有吃过苦头和那些没有吃够苦头的C++程序员: