这里有人用过c++的“placement new”吗?如果有,为什么?在我看来,它只在内存映射硬件上有用。


当前回答

我还有一个想法(它对c++ 11有效)。

让我们看看下面的例子:

#include <cstddef>
#include <cstdio>

int main() {
    struct alignas(0x1000) A {
        char data[0x1000];
    };

    printf("max_align_t: %zu\n", alignof(max_align_t));

    A a;
    printf("a: %p\n", &a);

    A *ptr = new A;
    printf("ptr: %p\n", ptr);
    delete ptr;
}

使用c++ 11标准,GCC给出以下输出:

max_align_t: 16
a: 0x7ffd45e6f000
ptr: 0x1fe3ec0

PTR没有正确对齐。

对于c++ 17标准和更高级的标准,GCC给出了以下输出:

max_align_t: 16
a: 0x7ffc924f6000
ptr: 0x9f6000

PTR对齐正确。

据我所知,c++标准在c++ 17之前不支持过对齐的new,如果你的结构的对齐大于max_align_t,你就会遇到问题。 要在c++ 11中绕过这个问题,可以使用aligned_alloc。

#include <cstddef>
#include <cstdlib>
#include <cstdio>
#include <new>

int main() {
    struct alignas(0x1000) A {
        char data[0x1000];
    };

    printf("max_align_t: %zu\n", alignof(max_align_t));

    A a;
    printf("a: %p\n", &a);

    void *buf = aligned_alloc(alignof(A), sizeof(A));
    if (buf == nullptr) {
        printf("aligned_alloc() failed\n");
        exit(1);
    }
    A *ptr = new(buf) A();
    printf("ptr: %p\n", ptr);
    ptr->~A();
    free(ptr);
}

在这种情况下PTR是对齐的。

max_align_t: 16
a: 0x7ffe56b57000
ptr: 0x2416000

其他回答

我认为任何答案都没有强调这一点,但新位置的另一个好例子和用法是减少内存碎片(通过使用内存池)。这在嵌入式和高可用性系统中特别有用。在最后一种情况下,这一点特别重要,因为对于一个必须运行24/365天的系统来说,没有碎片是非常重要的。此问题与内存泄漏无关。

Even when a very good malloc implementation is used (or similar memory management function) it's very difficult to deal with fragmentation for a long time. At some point if you don't manage cleverly the memory reservation/release calls you could end up with a lot of small gaps that are difficult to reuse (assign to new reservations). So, one of the solutions that are used in this case is to use a memory pool to allocate before hand the memory for the application objects. After-wards each time you need memory for some object you just use the new placement to create a new object on the already reserved memory.

这样,一旦应用程序启动,就已经预留了所需的所有内存。所有新的内存预留/释放都将分配到已分配的池中(您可能有几个池,每个池对应一个不同的对象类)。在这种情况下不会发生内存碎片,因为没有间隙,您的系统可以运行很长时间(数年)而不会出现内存碎片。

我在实践中看到过这种情况,特别是在VxWorks RTOS中,因为它的默认内存分配系统受到了很多碎片的影响。因此,通过标准的new/malloc方法分配内存在项目中基本上是被禁止的。所有的内存预留都应该到一个专用的内存池中。

脚本引擎可以在本机接口中使用它来从脚本分配本机对象。有关示例,请参阅Angelscript (www.angelcode.com/angelscript)。

一般来说,放置新是为了摆脱“正常新”的分配成本。

我使用它的另一个场景是,我想要访问一个仍待构造的对象的指针,以实现每个文档的单例。

它被std::vector<>使用,因为std::vector<>通常分配比vector<>中的对象更多的内存。

我用它创建了一个Variant类(例如,一个对象可以表示一个单独的值,这个值可以是许多不同类型中的一个)。

如果Variant类支持的所有值类型都是POD类型(例如int, float, double, bool),那么带标签的C风格的联合就足够了,但如果你想要一些值类型是c++对象(例如std::string), C的联合特性就不行,因为非POD数据类型可能不会被声明为联合的一部分。

因此,我分配了一个足够大的字节数组(例如sizeof(the_largest_data_type_I_support)),并使用placement new在该区域初始化适当的c++对象,当Variant被设置为持有该类型的值时。(当然,当切换到不同的数据类型时,我事先手动调用对象的析构函数)