【C++】new和delete表达式与内存管理
new和delete表达式可以用来动态创建和释放单个对象,也可以用来动态创建和释放动态数组。
定义变量时,必须指定其数据类型和名字。而动态创建对象时,只需指定其数据类型,而不必为该对象命名。new表达式返回指向新创建对象的指针,我们通过该指针访问对象:
1 int i; //named, uninitizlized int variable
2 int *pi = new int; //pi points to dynamically allocated unnamed int
1 int i(1024); //value of i is 1024
2 int *pi = new int(2014); //object to which pi is 1024
3 string s(10,'9'); //value of s is '9999999999'
4 string *ps = new string(10, '9'); //*ps is '9999999999'
C++使用值初始化的方式语法规则初始化动态创建的对象。如果提供了初值,new表达式分配到所需要内存后,用给定的初值初始化该内存。
2. 动态创建对象的默认初始化
如果不显式初始化。动态创建的对象与在函数内定义的变量初始化方式相同。对于类类型对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化
1 string *ps = new string; //initialized to empty string
2 int *pi = new int; //pi points to uninitialized int
正如我们几乎总要初始化定义为变量的对象一样,在动态创建对象时,(几乎)总是对它做初始化也是一个好方法。
同样也可以对动态创建的对象做值初始化,规则是:对于类类型对象,调用该类的默认构造函数;内置类型初始化为0;
1 string *ps = new string() //initialized to empty string
2 int *pi = new int(); //pi points to int value-initialized to 0
3 cls *ps = new cls(); //pc points to value-initialized object of type cls
可以看出,对于类类型的对象,无论程序是明确地不初始化还是要求进行初始化,都会自动调用其默认构造函数初始化该对象。而对于内置类型或没有定义默认构造函数的类型,采用不同初始化方式则有明显的差别:
1 int *pi = new int; //pi points to uninitialized int
2 int *pi = new int(); //pi points to an int value-initialized to 0
第一个语句的int型变量没有初始化,而第二个语句的int型变量初始化为0。
1 int *ip = 0;
2 delete pi; //ok: always ok to delete a pointer that is equal to 0
5. 在delete之后,重设指针的值
执行语句 delete p; 后,p变成没有定义。在很多机器上,尽管p没有定义, 但仍然存放了它之前所指向对象的地址,然而p所指向的内存已经释放,因此p不再有效。
删除指针后,该指针编程悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不存在了。悬垂指针往往导致程序错误,而且很检测出来。
一旦删除了指针指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。
6.动态内存的管理、
(1)删除指向动态分配内存的指针失败,因为无法将该块内存返回给自由存储区,删除动态分配内存失败称为“内存泄露”。内存泄露很难发现,一般需要等应用程序运行一段时间后,耗尽了所有内存空间时,内存泄露才会显露出来。
(2)读写已删除的对象,如果删除指针所指向的对象之后,将指针置为0,则比较容易检测出这类错误。、
(3)对同一内存空间使用两次delete表达式。当两个指针指向同一个动态创建的对象,删除就会发生错误,如果在其中一个指针上做delete运算,则该对象的内存空间返回给自由存储区,然后接着delete第二个指针,此时则自由存储区可能被破坏。
7. new和delete的底层原理
当你使用new时,有两件事情发生。第一,内存被分配出来(通过operator new的函数)。第二,针对此内存会有一个(或更多)构造函数被调用。
当你使用delete时,也有两件事情发生:针对此内存会有一个(或更多)的析构函数被调用,然后内存才能被释放(通过名为:operator delete的函数)。
创建动态数组:
3. 动态空间的释放
动态分配的空间必须释放,否则,内存最终将会被耗尽。如果不再需要使用动态创建的数组,程序员必须显式地将其占用的存储空间返回给程序的堆区。C++语言为指针提供了delete[] 表达式释放指针所指向的数组空间:
delete []pia;
该语句回收了pia所指向的数组,把相应的内存返回给堆。在关键字delete和指针之间的方括号是必不可少的;它告诉编译器该指针指向的是堆区的数组,而并非是单个对象。
理论上,回收数组时缺少方括号对,至少会导致运行时少释放了内存空间,从而产生内存泄露。对于某些系统或元素类型,有可能会带来更严重的运行时错误。因此,在释放动态内存时千万不能忘了方括号对
更多解释详见《Effective C++》条款16。
malloc/free和new/delete的区别:
1) malloc和free是C/C++语言的标准库函数,new/delete是C++的运算符。
2) malloc/free需要库文件支持,new/delete不需要。
3) new自动计算需要分配空间的大小,malloc需要手工计算字节数
4)new是类型安全的,而malloc不是,比如:
int* p = new float[2]; //编译时指出错误 int* p = (int*)malloc(2*sizeof(double)); //编译时无法指出错误
5)new调用operator new分配足够的空间,并调用相关对象的构造函数,而malloc不能调用构造函数;delete将调用该实例的析构函数,然后调用类的operator delete,以释放该实例占用的空间,而free不能调用析构函数。