有这样的代码:
#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
为什么可以在堆栈上动态分配内存? (我认为堆是正确的地方)。并且,为什么在这种情况下删除操作符返回错误,但新的操作符工作?
答案 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 new
:http://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.
}
正如您所看到的,内存和对象生命周期的分离增加了很多灵活性:我们可以将动态对象放在自动内存中,或者自己负责分配并重复使用内存来重复对象构造。标准new
和delete
表达式组合了分配和构造,但这只是最常用操作的快捷方式。原则上,你可以完全自由地分别处理内存和对象的生命周期。
这个想法支持 allocators 的概念,它是C ++标准库中的核心概念。