[关闭]
@rg070836rg 2015-08-16T15:11:44.000000Z 字数 2544 阅读 2938

c++ 中new与delete的探究以及小问题

data_structure


返回目录


一、new运算符

自由存储为 type-name 的对象或对象数组分配内存,并将已适当分类的非零指针返回到对象。

1.1、new 的工作方式

包含new运算符的表达式执行三类操作:

new 运算符调用函数 operator new,为数组以及基本类型对象分配存储
类对象可基于每个类定义其自己的 operator new 静态成员函数。

1.2、关于如何分配内存量

当编译器遇到用于分配类型 type 的对象的 new 运算符时,它将发布对 type::operator new( sizeof( type ) ) 的调用。因此,new 运算符可以为对象分配正确的内存量。

1.3、关于分配失败

如果不成功,则 new 将返回引发异常(std::bad_alloc)
例如下面的测试:

  1. #include <iostream>
  2. using namespace std;
  3. #define NUM 500000000
  4. int main() {
  5. int *p = new int[NUM];
  6. if (p == 0x0) {
  7. cout << "内存不足" << endl;
  8. system("pause");
  9. }
  10. else
  11. {
  12. cout << "分配成功" << endl;
  13. system("pause");
  14. }
  15. return 0;
  16. }

在VS下面,只会返回异常如下图:

此处输入图片的描述

当调整数据较小时(350000000),可以分配成功.
此处输入图片的描述

后修改测试代码,

  1. for (int i = 0; ;i++)
  2. {
  3. int *p = new int[10000];
  4. if (p == 0x0) {
  5. cout << "内存不足" << endl;
  6. system("pause");
  7. }
  8. }

发现,少量多次分配,基本不会出现大问题。程序可以分配到2G内存之后,报出std::bad_alloc异常。

VC平台

现在换到VC平台,调用分配大块内存,默认为抛出0,而不是异常。
此处输入图片的描述

1.4、解决方法

来自微软中国
处理失败的内存分配要求的方法:
编写自定义恢复例程来处理此类失败,然后通过调用 _set_new_handler 运行时函数来注册您的函数。


二、delete 运算符

2.1、用处

用于删除用new申请的空间,释放空间块。

2.2、注意点

  • 使用对象的 delete 运算符释放其内存。 在对象中删除后取消引用指针的程序,可能会有不可预知的结果或崩溃。
  • 当 delete用于释放C++类对象的内存时,对象的析构函数会在释放对象内存前调用 (如果对象具有析构函数)。
  • 如果对 delete运算符的操作的是一个可修改的左值,那么在对象删除后其值是未定义的。

三、其他问题

3.1在使用deltet后,需要把指针置空

如:

  1. delete L.head;
  2. L.head=NULL;

那么问题来了:为什么在delete L.head后,还需要赋值为空,是否多余?

测试:

  1. int main() {
  2. int *p = new int;
  3. *p=5;
  4. delete p;
  5. p=NULL;
  6. }

1、在执行了new之后,返回一个地址给p,
此处输入图片的描述
2、然后,p赋值5;
此处输入图片的描述
3、接下来delete p;值变成未知值。
此处输入图片的描述
但注意!这边并没有把p给释放掉,也就是说,p仍然指向了,一个内存单元,与之前的区别在于,原来那个单元是我自己申请的,属于我管,但是,现在我不想要了, 把他扔掉了,那这块就不归我管了,就好比把房子卖了,保存了钥匙。实际上,程序“应该”已经不具备管理这个内存单元的权利,但是我却没把权利完全给交出去一样.所以,一旦有其他程序,利用了这个内存单元(好比其他人把房子买了过去),而我恰巧操作不当,使用了这个本身不归我管理的“钥匙”,那么就会造成程序的紊乱。(换句话说,就是指向内存块不合法!)

4、因此,我们需要执行第四步,置空。
此处输入图片的描述
这样,就不会产生野指针,造成紊乱。


3.2、悬垂指针

3.2.1定义

当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针。

3.2.2成因

1、 在许多编程语言中,显示地从内存中删除一个对象或者返回时通过销毁栈帧,并不会改变相关的指针的值。该指针仍旧指向内存中相同的位置,即使指针所指的内容已经被删除。一旦误操作该指针,会导致错误。
2、 当一个指针指向的内存被释放后就会变成悬垂指针。可以避免这个问题的一种方法是在释放它的引用后把指针重置为NULL。

3.2.3来自百科的一种解决方法

引入智能指针可以防止垂悬指针出现。一般是把指针封装到一个称之为智能指针类中,这个类中另外还封装了一个使用计数器,对指针的复制等操作将导致该计数器的值加1,对指针的delete操作则会减1,值为0时,指针为NULL


3.3内存泄露

这是与悬垂指针相对立的一个问题。

3.3.1定义

memory leak指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

3.3.2演示

举个简单的例子:
定义一个节点类,然后一次生成5个节点。进行一些操作。

  1. struct Node
  2. {
  3. int a;
  4. Node* next;
  5. };
  6. void main()
  7. {
  8. Node *pHead=new Node;
  9. pHead->a=0;
  10. pHead->next=NULL;
  11. Node *p=pHead;
  12. for (int i=0;i<5;i++)
  13. {
  14. p->next=new Node;
  15. p=p->next;
  16. p->a=i+1;
  17. }
  18. p->next=NULL;
  19. while (pHead)
  20. {
  21. cout<<pHead->a<<endl;
  22. pHead=pHead->next;
  23. }
  24. }

程序理所当然的运行了:
此处输入图片的描述

结果看上去也没错,输出了5个值。
此处输入图片的描述

但是请注意,如果还有后续操作,会发现,根本找不到之前的元素了!
此处输入图片的描述

这就产生了内存泄露,那些元素除非是在程序运行结束,否则空间根本不可能被释放!这是小程序,看不出来,如果在一个大程序中也犯如此低级错,那么程序将消耗巨额内存。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注