首页 > 代码库 > 布局new操作符引发的有关析构函数的探索与总结

布局new操作符引发的有关析构函数的探索与总结

<布局new操作符——不负责分配内存>

    布局new操作符能够使我们在分配内存时指定位置。

    如下代码交代了相关语法:

 

      char *buffer = new char[BUF];

      JustTesting *p1, *p2;

      p1 = new (buffer) JustTesting;  //额外加入的(buffer)用来指定位置

 

     这意味着将JustTesting的一个实例分配在了以buffer为头地址的内存空间中,并用指针p1指向这个实例。

     换句话说,我们将JustTesting实例化在了已经被开辟的内存空间buffer中。

  这时候我们不禁要问一句,我们在同一个位置开辟了两次内存空间,这合理吗?

  并且在相关代码即将结束时,我们需用如下代码析构对象并释放内存。

 

     pc->~JustTesting();

    delete[]buffer;

 

  对应的,如果析构函数的作用是释放内存空间,我们对同一内存空间释放了两次,这又合理吗?

  一个合理的猜测是内存空间可被重叠分配和释放,只要使用的合理,不存在越界等问题,就是合乎语法的。

  但是问题远没我们猜测的这么复杂。

 

  首先需要说明的是,布局new操作符是一个不折不扣的骗子。它的责任仅仅在指定的内存空间(buffer)实例化出一个JustTesting的对象而已,并不负责分配内存。也就是说,我们在使用布局new操作符的时候,必须保证指定的地址是安全的。这正是{Char *buffer = new char[BUF];}这行代码的作用。

 

 

<析构函数——不负责回收内存>

  其次,显示调用析构函数并不会起到释放内存的作用,它和普通的函数并无区别,仅仅将其中代码运行一遍而已。

  那为什么还要显示调用它呢?反正真正起到释放内存作用的是delete。释放之后这块内存又会被重新利用并覆盖。

  这一点都没错,但是当构造函数中有new操作符分配的额外内存时情况就不一样了。这将导致内存泄露。所以我们显示调用析构函数正是为了释放在构造函数中额外“借来”的内存。

  结论就是,构造函数和析构函数的调用并没有开辟或回收内存的作用。

 

 

<内存由谁分配?>

  那么我们使用如下代码时,确实分配了内存:

 

    JustTesting one;

 

  没错,或许你还能这样另一种确实分配了内存的情况:

 

    *p2 = new JustTesting;

 

  与之对应的就有:

 

    delete p2; //我们总是以这种方式去析构指针对象

 

  这下明白些了,在声明一个对象的时候隐含了new过程。对应地,删除对象的时候(例如超出作用域)也隐含了delete过程。(注意,new和布局new有着本质区别。)

 

 

<最后值的注意的一点>

  尽管构造和析构函数可以显示调用,但是当其中包含new和delete时,多次使用是危险的。它们会造成内存泄露和重复释放相同内存区。