在阅读C ++ Primer Plus时,我几乎没有关于新位置的问题。
书中的示例代码如下:
class JustTesting{
private:
string words;
int number;
public:
JustTesting(const string & s = "Just Testing", int n = 0){
number = n;
words = s;
//some code here
}
~JustingTesting(){}
};
char * buffer = new char[BUF]; //get a block of memory
JustTesting *pc1, *pc2;
pc1 = new (buffer) JustTesting; //Place object in buffer
pc2 = new JustTesting("Heap1",20); //Place object on heap
//some code
JustTesting *pc3, *pc4;
pc3 = new (buffer) JustTesting("Bad Idea", 6);
pc4 = new JustTesting("Heap2", 10);
//some code
delete pc2; //free Heap1
delete pc4; //free Heap2
pc3->~JustTesting(): //Does the order of these two destructor call
pc1->~JustTesting(); // matters?
delete[] buffer; //free buffer
作者说,我们不能使用
delete pc1;
或
delete pc3;
删除他们指向的对象,因为delete与new一起使用但不与placement new一起使用。例如,指针pc3不接收new返回的地址,delete pc3
将导致运行时错误。
问题是:
首先,pc3指向的对象是否会覆盖pc1指向的对象?如果不是,两个不同的对象如何保持在同一个地址中。如果是,为什么我们仍然可以显式调用析构函数(pc1->~JustTesting();
)来释放对象的内存。
问题二:这两个显式析构函数的顺序是否有问题?
问题三:&#34的含义是什么;指针pc3没有收到新"?返回的地址?新的不同地址是否由赋值给出的地址返回?
谢谢!
答案 0 :(得分:5)
一切都是对的,直到......
select *
from test
where meta->>'name' = 'Hello';
这会调用未定义的行为(不?)。您已经在pc3 = new (buffer) JustTesting("Bad Idea", 6);
构建了JustTesting
类型的对象,但您还没有破坏它!同时,您在同一位置创建但另一个对象。然后,第一个对象被破坏(虽然,在标准的思想中,它仍然存在于一个平行的宇宙中)。
您无法对buffer
尚未分配(和构建)的任何指针执行delete
。同样,您只能使用operator new
销毁和取消分配operator new[]
创建的数组。
现在,“placement new”只是一个直接调用构造函数的奇特名称。因此,operator delete[]
只是调用new(buff) Type(...)
的构造函数,Type
设置为this
。而且,与上述内容相似,你只能破坏已构建的内容。
如果您使用自动存储,buff
或任何其他隐式RAII一致的方式,负责自动分配,构造和破坏您的对象(或者当您指定它时),然后调用在这样的上下文中,对象的析构函数将导致析构函数被调用两次, aka 未定义的行为。
现在,发生你(我会再重复一遍吗?你!)决定何时以及如何获取对象的内存,然后环境没有改变猜测何时销毁或解除分配对象。因此,一旦你明确地调用了对象的析构函数,曾经包含它的内存由你负责释放,不知何故,如果有的话。
想一想。形式operator new
的表达式可以完美地实现为......
ptr = new X(...)
ptr = malloc(sizeof(X));
new(ptr) X(...);
变成......
operator delete
答案 1 :(得分:0)
问题是:首先,pc3指向的对象是否会覆盖pc1指向的对象?“
这很危险,两个对象都会尝试共享同一块内存。
如果是,为什么我们仍然可以显式调用析构函数(pc1-> ~JustTesting();)来释放对象的内存。
这不会释放通过placement new创建的对象的内存。它只是调用对象的析构函数。传统的删除操作符将调用析构函数,然后通过假设正常分配来尝试释放内存。在这个例子中,由于已经引发的共享问题,它将有效地尝试两次调用该内存块上的析构函数。
问题二:这两个显式析构函数的顺序是否有问题?
不,在任何一种情况下,第二次通话都可能有问题。
问题三:“指针pc3没有收到新的返回地址”是什么意思?地址是否按照赋值给出的地址以不同的方式返回?
new返回的地址是堆中新分配的内存块(假设默认的新运算符未被覆盖)。 placement new返回的地址只是你给它的内存块的地址。
答案 2 :(得分:0)
首先,pc3指向的对象是否会覆盖pc1指向的对象?
是。它们都将存储在buffer
指向的地址。 This may be undefined behaviour,无论如何通常都是个坏主意(除非你确定该类的析构函数没有做任何重要的事情)。
如果是,为什么我们仍然可以显式调用析构函数(pc1-> ~JustTesting();)来释放对象的内存。
最有可能的是,pc1
和pc3
指向同一位置。我不确定这是否有保证,但我无法想象为什么在任何实施中都不会出现这种情况。
如果pc1
和pc3
指向同一位置,那么这个问题就像问以下代码的工作原理一样:
char *p1 = new char[50];
char *p2 = p1;
delete [] p2;
指针pc3没有收到new返回的地址是什么意思?地址是否按照赋值给出的地址以不同的方式返回?
可能是措辞不好。显然,pc3
会收到new
返回的地址 - 您可以在代码中看到它:
pc3 = new // ... other stuff ...
我怀疑他们的意思是new
地址不是分配。通常new
分配一块内存来保存一个对象,然后在该内存块中构造一个对象。使用放置new
,它只在提供的内存块中构造一个对象。
同样,delete
将销毁指针传递给它的对象,然后释放先前在该地址分配的内存块。 delete
未分配new
的对象是未定义的行为 - 请考虑JustTesting a("Object on stack", 30); delete &a;
。
如果你很幸运,delete pc3
可能与pc3->~JustTesting(); delete [] buffer;
具有相同的效果,因此它可能不会崩溃 - 尽管那时{{ 1}}因为你要删除一个对象两次会崩溃。你绝对不应该依赖它。唯一安全的做法是不delete [] buffer;
使用展示位置delete
分配的对象。
答案 3 :(得分:0)
问题是:首先,pc3指向的对象是否会覆盖pc1指向的对象?
是
如果是,为什么我们仍然可以显式调用析构函数(pc1-> ~JustTesting();)来释放对象的内存。
pc
尚不存在,任何调用它的函数都是UB。
问题二:这两个显式析构函数的顺序是否有问题?
你根本不应该一起打电话。
问题三:“指针pc3没有收到新的返回地址”是什么意思?新的不同地址是否由赋值给出的地址返回?
我认为它应该意味着“指针pc3没有收到新分配的地址”。 placement new只返回您传递给它的地址。