析构函数剖析一

      析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。


      显式的调用析构函数是一件非常危险的事情,我们自己所谓的显式调用析构函数,实际上只是调用了一个成员函数,并没有真正意义上的让对象“析构”。
     

为了理解这个问题,我们必须首先弄明白“堆区”和“栈区”的概念。

      堆区(heap) —— 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
      栈区(stack)—— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。


      如果对象被建立在堆上,系统就不会自动调用。所以,如果我们在析构函数中有清除堆数据的语句,调用两次意味着第二次会试图清理已经被清理过了的,根本不再存在的数据!这是件会导致运行时错误的问题,并且在编译的时候不会告诉你!
      显式调用的时候,析构函数相当于的一个普通的成员函数

      举个例子说明一下:

           Fred *p=new Fred();
           delete p;//自动调用p->~Fred
      但是如果我们将上一条语句改为:p->~Fred();呢。会出现什么情况呢?
      因为显示调用析构函数不会释放Fred对象本身的内存,也就是栈内存,所以不要这么做,记住delete做了2件事情:调用析构函数和回收内存。

 

      编译器隐式调用析构函数,如分配了堆内存,显式调用析构的话引起重复释放堆内存的异常。
      把一个对象看作占用了部分栈内存,占用了部分堆内存(如果申请了的话),这样便于理解这个问题。
      1. 系统隐式调用析构函数的时候,会加入释放栈内存的动作(即会清空局部变量等栈中数据,而堆内存则由用户手工的释放)。
      2. 用户显式调用析构函数的时候,只是单纯执行析构函数内的语句,不会释放栈内存,摧毁对象。
      3. 很罕见的例外在于使用布局new(ie: placement new)的时候,在delete设置的缓存之前,需要显式调用的析构函数,这实在是很少见的情况。

      什么是定位new呢?定位放置new有很多的作用。最简单的用处就是将对象放置在内存中的特殊位置。这是依靠 new表达式部分的指针参数的位置来完成的。
      #include <new> // 必须 #include 这个,才能使用 "placement new"
      #include "Fred.h" // class Fred 的声明
      void someCode()
      {
           //在内存中创建了一个sizeof(Fred)字节大小的数组,足够放下 Fred 对象。                char memory[sizeof(Fred)];

           // 创建了一个指向这块内存的首字节的place指针
           void* place = memory;

           Fred* f = new(place) Fred(); // Line #1注意看这里。下面有解释。
           // The pointers f and place will be equal
           // ...

      }

      Line #1本质上只是调用了构造函数 Fred::Fred(). Fred构造函数中的this指针将等于place。因此返回的 f 将等于place。

      注意:万不得已时才使用“placement new”语法。只有当你真的在意对象在内存中的特定位置时才使用它。例如,你的硬件有一个内存映象的 I/O计时器设备,并且你想放置一个Clock对象在那个内存位置。

      但是这个是很危险的:你要独自承担这样的责任,传递给“placement new”操作符的指针所指向的内存区域必须足够大,并且可能需要为所创建的对象进行边界调整。编译器和运行时系统都不会进行任何的尝试来检查你做的是否正确。如果 Fred 类需要将边界调整为4字节,而你提供的位置没有进行边界调整的话,你就会亲手制造一个严重的灾难(如果你不明白“边界调整”的意思,那么就不要使用placement new语法)。

      你还有析构放置的对象的责任。这通过显式调用析构函数来完成:
      void someCode()
      {
           char memory[sizeof(Fred)];
           void* p = memory;
           Fred* f = new(p) Fred();
           // ...
           f->~Fred(); // 显式调用定位放置的对象的析构函数
      }

      这是显式调用析构函数的唯一时机。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值