动态在堆栈上分配内存

3

有这样的代码:

#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

为什么可以在堆栈上动态分配内存?(我认为堆是正确的地方)。此外,为什么在这种情况下delete运算符会返回错误,但new运算符却可以工作?


你正在使用放置 new,因此你正在重复使用 a 的内存来放置 p。 - Tony The Lion
1
它并不是真正的动态,因为您可以使用的最大空间是“sizeof(int)”。 - James
请查看Linux alloca。 - tigertang
4个回答

8

这是使用放置 new 语法。放置 new 不会分配内存,而是在特定位置构造一个对象的方法。在此示例中,内存来自堆栈。它不必如此。delete 会出现问题,因为您没有为内存进行 new 操作。

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


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

这被称为就地构造(placement-new)。它不会分配内存。它在a的同一块内存中构造对象。在就地构造中,是用户指定new运算符构造对象的内存区域。在你上面的代码中,你通过在new关键字后面写入(&a)表达式来指定内存区域。由于&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

3
这被称为“定位new”:http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10 你可以选择向new传递一个地址,它只会调用对象的构造函数(如果有的话)。由于没有分配内存,使用delete释放它将是一个错误。只需调用对象的析构函数(如果有的话),就完成了操作。

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
}

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

{
    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表达式结合了分配和构造,但这只是最常用操作的快捷方式。原则上,您完全可以单独处理内存和对象生命周期。
这个想法支持C++标准库中的核心概念——分配器

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接