在堆栈上动态分配内存

时间:2011-09-18 14:22:41

标签: c++

有这样的代码:

#include <iostream>

int main()
{
    int a;
    int* p = new (&a) int(2);
    std::cout << a << std::endl;
    // delete p; error BLOCK TYPE IS INVALID
    std::cin.get();
    return 0;
}

输出结果为:

2

为什么可以在堆栈上动态分配内存? (我认为堆是正确的地方)。并且,为什么在这种情况下删除操作符返回错误,但新的操作符工作?

4 个答案:

答案 0 :(得分:8)

这是使用展示位置新语法。 Placement new根本不分配内存,而是一种在特定位置构造对象的方法。在此示例中,内存来自堆栈。它不必。删除有问题,因为你没有新的内存。

有一些方法可以从堆栈(alloca)动态分配内存,但这不是这里发生的事情。

答案 1 :(得分:7)

int* p = new (&a) int(2);

这称为placement-new。它不分配内存。它在a的同一内存中构造对象。在placement new中,用户指定new运算符构造对象的内存区域。在上面的代码中,您可以通过在(&a)关键字后面写new表达式来指定内存区域。由于&a不是动态分配的内存,因此您无法delete

delete p; //runtime-error

它会产生运行时错误,它会尝试删除变量a所在的内存。

但是,如果您动态分配内存,则可以删除它。让我们假设,A是一类,那么你应该这样做:

char *buffer = new char[sizeof(A)]; //allocate memory of sizeof(A);

///ASSUMPTION: the buffer is properly align as required by the type A
//use placement-new to construct an object at the specified memory region
A *pA = new (buffer) A(/*..parameters..*/); 

//...

//incorrect way to delete the memory!
//delete pA; //incorrect

//before deleting the memory you should be calling the destructor explicitly as
pA->~A(); //call the destructor explicitly - must do it

//now deallocate the memory as
delete []buffer;

这是placement-new的最简单示例,仅解释语法。但故事并没有在这里结束;它是开始并使其正常工作,buffer指向的内存必须正确对齐对象类型,在上面的例子中,我只是假设。在实际代码中,你不能做出这样危险的假设。现在阅读这个FAQ:

答案 2 :(得分:3)

这称为placement newhttp://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10

您可以选择将地址传递给new,它只会调用对象的构造函数(如果有的话)。因为没有分配内存,所以用delete释放它是错误的。只需调用对象的析构函数(如果有的话)就完成了。

答案 3 :(得分:2)

C ++分离了内存分配对象生存期的概念。与C相比,这可能是该语言最重要的“新”方面之一。在C中没有这样的区别,因为变量完全由它们的内存决定,而在C ++对象中有一个更抽象的“状态”概念。与底层记忆不同。

让我们先看看记忆:

{
    char buf[100];  // automatic allocation, scoped lifetime
}

{
    void * p = std::malloc(100);      // dynamic allocation, manually managed
    void * q = ::operator new(100);   // essentially identical

    // ...

    ::operator delete(q);             // manual deallocation
    std::free(p);                     // ditto
}

另一方面,对象的生命周期是一个单独的主题:

{
    Foo x;    // automatic storage, scoped lifetime.
              // Implies automatic memory allocation for sizeof(Foo) bytes.
}

{
    Foo * px = ::new Foo;      // dynamic storage, manual lifetime,
                               // implies dynamic allocation via ::operator new()

    Foo * py = ::new (q) Foo;  // dynamic storage and manual lifetime, uses memory at q

    // ...

    delete px;                 // destroy object _and_ deallocate memory

    py->~Foo();                // destroy object. Memory was never our business to start with.
}

正如您所看到的,内存和对象生命周期的分离增加了很多灵活性:我们可以将动态对象放在自动内存中,或者自己负责分配并重复使用内存来重复对象构造。标准newdelete表达式组合了分配和构造,但这只是最常用操作的快捷方式。原则上,你可以完全自由地分别处理内存和对象的生命周期。

这个想法支持 allocators 的概念,它是C ++标准库中的核心概念。