C ++中新增的展示位置

时间:2015-10-12 01:37:00

标签: c++

在阅读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没有收到新"?返回的地址?新的不同地址是否由赋值给出的地址返回?

谢谢!

4 个答案:

答案 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();)来释放对象的内存。

最有可能的是,pc1pc3指向同一位置。我不确定这是否有保证,但我无法想象为什么在任何实施中都不会出现这种情况。

如果pc1pc3指向同一位置,那么这个问题就像问以下代码的工作原理一样:

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只返回您传递给它的地址。