<布局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时,多次使用是危险的。它们会造成内存泄露和重复释放相同内存区。