C++指针delete后还要置为null

众所周知,最开始我们用new来创建一个指针,那么等我们用完它之后,一定要用delete将该指针删掉。但是,值得注意的是,难道就仅仅是删除这个指针这么简单的么?下面,我们用一个程序来说明这个问题:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3. int main()   
  4. {  
  5.     int *p=new int;   
  6.     *p=3;  
  7.     cout<<"将3赋给p的地址后,指针p读取的值:"<<*p<<endl;  
  8.     delete p;  
  9.     cout<<"删除空间后,指针p读取的值:"<<*p<<endl;  
  10.     long *p1=new long;  
  11.     *p1=100;  
  12.     cout<<"创建新空间后,指针p中保存的地址:"<<p<<endl;  
  13.     cout<<"指向新空间的指针p1保存的地址:"<<p1<<endl;  
  14.     *p=23;  
  15.     cout<<"将23赋给p的地址后,指针p读取的值:"<<*p<<endl;  
  16.     cout<<"将23赋给p的地址后,指针p1读取的值:"<<*p1<<endl;  
  17.     delete p1;  
  18.     return 0;  
  19. }  
在上面这个程序中,我们在第8行就将指针p利用delete删掉了。但是,我们来看看程序的输出结果:


对照着上面的程序,我们来分析一下这个输出。首先,我们在程序的第5行初始化了一个指针p。之后输出指针p读取的值。由于第6行的原因,程序肯定会输出3了。之后,我们在程序的第8行删除了这个指针p。但是我们惊奇的发现,在程序的第9行竟然可以输出指针p读取的值。我们不是已经把它删了么?其实不然,debug,上图:



从监视窗口中,我们可以看见虽然程序的第8行已经将指针p删除了,但是在监视窗口中p仍然存在,只是*p所指向的值不再是原来的3了,而是一个随机数。这里就说明了一个非常重要的概念:我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身。

      然后我们接着往下分析。在程序的第10行我们又创建了一个long型的指针p1。在12行与13行的输出中我们惊奇地发现,指针p保存的地址居然和指针p1保存的地址一模一样!这个就说明了指针p和指针p1都指向内存的同一个地方!!!出现这种状况的原因其实是由于编译器。编译器默认将释放掉的内存空间回收然后分配给新开辟的空间。所以在第11行由于我们新开辟了一个可以保存long型变量的空间并且由p1来指向它,那么这里的p1指向的其实就是在程序第8行释放掉的内存空间,即p指向的内存空间!所以,这就导致了两个指针同时指向同一个内存空间。这是多不安全的一件事情啊!要知道,我们是把指针p删了的啊!如果再重新对*p进行赋值操作,那么不是会连着*p1一起改动么?

      果然,让我们担心的事情出现了。我们明明在程序的第11行中定义了*p1的值为100,但是在输出上面,指针p1读取的值竟然也是23。这个原因就是因为野指针p造成的。我们可以看到,在程序的第14行我们将23赋给了*p。又由于p和p1指向的是同一块内存单元,所以在这里相当于也将p1所指向的内存单元中的值(原来是100),改成了23!这样必然会导致程序的出错!

       那么我们就不禁要问了,对于这种由于野指针造成的问题,有没有解决的方法呢?答案当然是有的了。我们只需要牢记下面这句话:

在删除一个指针之后,一定将该指针设置成空指针(即在delete *p之后一定要加上: p=NULL)

      我们来看一下在stdio.h中关于关键字NULL的定义:

[cpp]  view plain  copy
  1. /* Define NULL pointer value */  
  2.     
  3. #ifndef NULL  
  4. #ifdef __cplusplus  
  5. #define NULL    0  
  6. #else  
  7. #define NULL    ((void *)0)  
  8. #endif  
  9. #endif  

 注意上面定义的第5行。这里其实就说明了NULL就是0。也就是说,我们在删除完指针p之后,一定要把它变成空指针!只有这样,才会杜绝上面程序中出现的野指针的错误。

p.s. 对于NULL的应用,我们不应该仅限于上面的方法,还可以应用NULL来判断指针是否初始化成功了,如下例if中的判断方法:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.     
  4. int main()  
  5. {  
  6.     int *p=new int;  
  7.     if (p==NULL)  
  8.     {  
  9.         //判断指针p是不是空指针,如果是空指针,那么程序在这里就应该报错  
  10.         //报错的方法有很多,比如说返回一个ERROR值:  
  11.         //return ERROR;  
  12.     }  
  13.     
  14.     //判断了操作成功之后我们才能进行一系列的操作  
  15.     //...  
  16.     
  17.     //用完指针p之后,一定要将其删掉。这样可以杜绝野指针的存在  
  18.     delete p;  
  19.     //删除指针p之后,一定要加上下面这句话,免得成为野指针  
  20.     p=NULL;  
  21. }  

好了,下次一定要记住,在分配空间给指针之后,一定要用 NULL 来判断一下是否成功了。然后在删除这个指针的时候,也要用 NULL 来赋给指针,杜绝成为野指针!
  • 110
    点赞
  • 175
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
C++智能指针详解 智能指针详解 智能指针内容很多,重点是基本⽤法。 #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_from_this<CBase> { public: virtual void f(){}//必须有个虚函数才能向上向下转换。 } typedef boost::shared_ptr<CBase> CBasePtr; class CChild: public CBase {} typedef boost::shared_ptr<CChild> CChildPtr; void main() { CBasePtr ptrBase = boost::make_shared<CBase>(); //CBasePtr ptrBase = CBasePtr(new CBase()); // 向下转换 CChildPtr ptrChild = boost::dynamic_pointer_cast<CChild>(ptrBase); // 向上转换 CBasePtr ptrXXX = ptrChild; // 普通转换 CChildPtr ptrXX = CChildPtr(dynamic_cast<CChild*>(ptrXXX.get())); } 暂时学会这些⽤法即可。 url: C++ 智能指针详解 ⼀、简介 由于 C++ 语⾔没有⾃动内存回收机制,程序员每次 new 出来的内存都要⼿动 delete。程序员忘记 delete,流程太复杂,最终导致没 有 delete,异常导致程序过早退出,没有执⾏ delete 的情况并不罕见。 ⽤智能指针便可以有效缓解这类问题,本⽂主要讲解参见的智能指针的⽤法。包 括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、、boost::weak_ptr、boost::intrusive_ptr。你可能会想,如 此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇⽂章后,我想你⼼⾥⾃然会有答案。 下⾯就按照顺序讲解如上 7 种智能指针(smart_ptr)。 ⼆、具体使⽤ 1、总括 对于编译器来说,智能指针实际上是⼀个栈对象,并⾮指针类型,在栈对象⽣命期即将结束时,智能指针通过析构函数释放有它管理的 堆内存。所有智能指针都重载了"operator->"操作符,直接返回对象的引⽤,⽤以操作对象。访问智能指针原来的⽅法则使⽤"."操作符。 访问智能指针包含的裸指针则可以⽤ get() 函数。由于智能指针是⼀个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指 针是否为空,需要这样判断:if (my_smart_object.get())。 智能指针包含了 reset() ⽅法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递⼀个对象,则智能指 针会释放当前对象,来管理新传⼊的对象。 我们编写⼀个测试类来辅助分析: class Simple { public: Simple(int param = 0) { number = param; std::cout << "Simple: " << number << std::endl; } ~Simple() { std::cout << "~Simple: " << number << std::endl; } void PrintSomething() { std::cout << "PrintSomething: " << info_extend.c_str() << std::endl; } std::string info_extend; int number; }; 2、std::auto_ptr std::auto_ptr 属于 STL,当然在 namespace std 中,包含头⽂件 #include<memory> 便可以使⽤。std::auto_ptr 能够⽅便的管理单个堆 内存对象。 我们从代码开始分析: void TestAutoPtr() { std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1 if (my_memory.get()) { // 判断智能指针是否为空 my_memory->PrintSomething(); // 使⽤ operator-> 调⽤智能指针对象中的函数 my_memory.get()->info_extend = "Addition";
C++智能指针的原理和实现 智能指针的原理和实现 ⼀、智能指针起因 ⼀、智能指针起因   在C++中,动态内存的管理是由程序员⾃⼰申请和释放的,⽤⼀对运算符完成:new和delete。   new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的指针;   delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。   使⽤堆内存是⾮常频繁的操作,容易造成堆内存泄露、⼆次释放等问题,为了更加容易和更加安全的使⽤动态内存,C++11中引⼊了智 能指针的概念,⽅便管理堆内存,使得⾃动、异常安全的对象⽣存期管理可⾏。智能指针主要思想是RAII思想,"使⽤对象管理资源",在类 的构造函数中获取资源,在类的析构函数中释放资源。智能指针的⾏为类似常规指针,重要的区别是它负责⾃动释放所指向的对象。   RAII是Resource Acquisition Is Initialization的简称,即资源获取就是初始化:   1.定义⼀个类来封装资源的分配与释放;   2.构造函数中完成资源的分配及初始化;   3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放;   4.如果对象是⽤声明的⽅式在栈上创建局部对象,那么RAII机制就会正常⼯作,当离开作⽤域对象会⾃动销毁⽽调⽤析构函数释放资 源。 ⼆、智能指针类型 ⼆、智能指针类型   智能指针C++11版本之后提供,包含在头⽂件<memory>中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四 种,其中auto_ptr已被弃⽤。   :拥有严格对象所有权语义的智能指针;   :拥有共享对象所有权语义的智能指针;   :到 shared_ptr 所管理对象的弱引⽤;   :拥有独有对象所有权语义的智能指针。 2.1 auto_ptr   auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr⾃⾝被销毁时删除该对象的智能指针,它可⽤于为动态分配的对象提供异常安 全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是⼀个轻量级的智能指针,适合⽤来管理⽣命周期⽐较短或者不会被 远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。   声明:   template< class T > class auto_ptr;   template<> class auto_ptr<void>; // 对类型void特化     成员函数:   (1) : 获得内部对象的指针;   (2) :释放被管理对象的所有权,将内部指针为空,返回内部对象的指针,此指针需要⼿动释放;   (3) :销毁内部对象并接受新的对象的所有权;   (4) :从另⼀auto_ptr转移所有权;   (5) 和:访问被管理对象。   注意事项:   (1) 其构造函数被声明为explicit,因此不能使⽤赋值运算符对其赋值,即不能使⽤类似这样的形式 auto_ptr<int> p = new int;   (2) auto_ptr 的对象所有权是独占性的,使⽤拷贝构造和赋值操作符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改;   (3) 基于第⼆条,因此不能将auto_ptr放⼊到标准容器中或作为容器的成员;   (4) auto_ptr不能指向数组,释放时⽆法确定是数组指针还是普通指针;   (5) 不能把⼀个原⽣指针交给两个智能指针对象管理,对其它智能指针也是如此。   auto_ptr是最早期的智能指针,在C++11 中已被弃⽤,C++17 中移除,建议使⽤unique_ptr代替auto_ptr。   简单实现: 1 template<class T> 2 class AutoPointer 3 { 4 public: 5 AutoPointer(T* ptr) 6 :mPointer(ptr){} 7 8 AutoPointer(AutoPointer<T>& other) 9 { 10 mPointer= other.mPointer; //管理权进⾏转移 11 other.mPointer= NULL; 12 } 13 14 AutoPointer& operator = (AutoPointer<T>& other) 15 { 16 if(this != &other) 17 { 18 delete mPointer; 19 mPointer = other.mPointer; //管理权进⾏转移 20 other.mPointer= NULL; 21 } 22 23 return *this; 24 } 25 26 ~AutoPointer() 27 { 28 delete mP

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值