美文网首页C++ 杂记
C++ 布局(placement)new 操作符

C++ 布局(placement)new 操作符

作者: 赵者也 | 来源:发表于2017-08-07 10:50 被阅读12次

new 负责在堆(heap)中找到一个足够满足要求的内存块。

new 操作符还有另一种变体,称为布局(placement)new 操作符,它能够让你指定要使用的位置。程序员可能使用这种特性来设置其内存管理规程或处理需要通过特定地址进行访问的硬件。

要使用布局 new 特性,首先需要包含头文件 new,它提供了这种版本的 new 操作符的原型;然后将 new 操作符用于提供了所需地址的参数。除了要指定参数外,句法与常规 new 操作符相同。具体地说,使用布局 new 操作符时,变量后面可以有方括号,也可以没有。

举例:

#include <QCoreApplication>
#include <new>
#include <QDebug>

namespace {

const int NUM_SIZE_PLACEMENT_BUFFER = 512;
const int NUM_TIMES_LOOP = 5;
char zdsBuffer[NUM_SIZE_PLACEMENT_BUFFER];
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    double *zdsPm1,*zdsPm2;
    int i;
    qDebug() << "new and placement new:";
    zdsPm1 = new double[NUM_TIMES_LOOP];
    zdsPm2 = new (zdsBuffer) double[NUM_TIMES_LOOP];

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        zdsPm2[i] = zdsPm1[i] = 1000 + 20.0 * i;
    }

    qDebug() << "Buffer addresses: " << " heap: " << zdsPm1
             << " static: " << (void*)zdsBuffer;

    qDebug() << "Buffer contents: ";

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        qDebug() << zdsPm1[i] << " at " << &zdsPm1[i] << ";"
                 << zdsPm2[i] << " at " << &zdsPm2[i] << ";";
    }

    qDebug() << "Calling Second time: ";

    double *zdsPm3,*zdsPm4;
    zdsPm3 = new double[NUM_TIMES_LOOP];
    zdsPm4 = new (zdsBuffer) double[NUM_TIMES_LOOP];

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        zdsPm4[i] = zdsPm3[i] = 1000 + 20.0 * i;
    }

    qDebug() << "Buffer contents: ";

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        qDebug() << zdsPm3[i] << " at " << &zdsPm3[i] << ";"
                 << zdsPm4[i] << " at " << &zdsPm4[i] << ";";
    }

    qDebug() << "Calling Third time: ";

    delete [] zdsPm1;

    zdsPm1 = new double[NUM_TIMES_LOOP];
    zdsPm2 = new (zdsBuffer + NUM_TIMES_LOOP * sizeof(double)) double[NUM_TIMES_LOOP];

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        zdsPm2[i] = zdsPm1[i] = 1000 + 20.0 * i;
    }

    qDebug() << "Buffer contents: ";

    for (i=0; i<NUM_TIMES_LOOP; ++i) {
        qDebug() << zdsPm1[i] << " at " << &zdsPm1[i] << ";"
                 << zdsPm2[i] << " at " << &zdsPm2[i] << ";";
    }

    delete [] zdsPm1;
    delete [] zdsPm3;

    return a.exec();
}

上面的示例在 QtCreator 中的输出:

QtCreator 中的输出

关于上面的例子的说明:
一. 布局 new 操作符确实将数组 zdsPm2 放在了数组 zdsBuffer 中。zdsPm2 和 zdsBuffer 的地址都是 0x603120。同时,常规 new 操作将数组 zdsPm1 放在很远的地方,其地址为 0x1fe4ab0,位于动态管理的堆中。
二. 第二个常规 new 操作符查找一个新的内存块,其起始地址为 0x1fe4960;但是第二个布局 new 操作符则分配与以前相同的内存块:起始地址为 0x603120 的内存块。布局 new 操作符使用传递给它的地址,它不跟踪哪些内存单元已被使用,也不查找未使用的内存块。这将一些内存管理的负担交给了程序员。例如:在第三次调用布局 new 操作符时提供了一个从数组 zdsBuffer 开头算起的偏移量,因此将分配新的内存:

zdsPm2 = new (zdsBuffer + NUM_TIMES_LOOP * sizeof(double)) double[NUM_TIMES_LOOP];

三. 关于是否使用 delete 来释放内存。对于常规 new 操作符,可以使用 delete 来释放内存中起始地址为 0x1fe4ab0 的内存块,因此接下来再次调用 new 操作符时,该内存块是可用的。然而,本例中 zdsBuffer 指定的内存是静态内存,而 delete 只能用于指向常规 new 操作符分配的内存,也就是说 zdsBuffer 数组位于 delete 管辖区之外。此时,使用 delete 将引发运行阶段错误。但是,如果 zdsBuffer 是使用常规 new 操作符创建的,便可以使用常规 new 操作符来释放整个内存块。

相关文章

网友评论

    本文标题:C++ 布局(placement)new 操作符

    本文链接:https://www.haomeiwen.com/subject/xtpolxtx.html