4

I have few questions about the placement new while reading the C++ Primer Plus.

The sample code in the book is like this:

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

The author says, we cannot use

delete pc1;

or

delete pc3;

to delete the objects they pointing to, because delete works in conjunction with new but not with placement new. For example, the pointer pc3 does not receive an address returned by new, and delete pc3 will result in a runtime error.

The questions are: first, will the object pointed to by pc3 overwrite the one pointed to by pc1? If no, how could two different objects stay in a same address. If yes, why we still possible to call the destructor explicitly(pc1->~JustTesting();) to free the object's memory.

question two: Does the order of these two explicit destructor call matter?

question three: What is the meaning of "the pointer pc3 does not receive an address returned by new"? Is the address returned by new different with the address given by assignment?

Thank you!

Curtis2
  • 106
  • 7

4 Answers4

5

Everything is right, until...

pc3 = new (buffer) JustTesting("Bad Idea", 6);

This invokes undefined behaviour (no?). You've already constructed an object of type JustTesting at buffer, but you have not destructed it! At the same time, you're creating yet another object at the same location. Then, the first object becomes corrupted (althought, in standardese minds, it still exists in a parallel universe).

You can't perform delete on a pointer to anything that hasn't been allocated (and constructed) by operator new. Similarly, you can only destroy and deallocate an array created by operator new[] with operator delete[].

Now, "placement new" is just a fancy name for a direct call to a constructor. Thus, new(buff) Type(...) is just a call to Types constructor with this set as buff. And, simmetric with what said above, you can only destroy what has been constructed.

If you use automatic storage, operator new, or whatever other implicit-RAII-conformant means that is responsible for allocating, constructing, and destructing your objects automatically (or when you specify it shall be done), then calling an object's destructor in such a context will lead to the destructor being called twice, a.k.a undefined behaviour.

Now, it happens that you (shall I repeat it again? you!) are the one who decides when and how to obtain the memory for the object, then the environment has no change of guessing when to either destroy or deallocate the object. Thus, once you call the object's destructor explicitly, the memory that once contained it is under your responsibility for being freed, somehow, if at all.

Think of it as such. An expression of the form ptr = new X(...) can be perfectly implemented as...

ptr = malloc(sizeof(X));
new(ptr) X(...);

And operator delete becomes...

ptr->~X();
free(ptr);
3442
  • 8,248
  • 2
  • 19
  • 41
  • Very helpful, but due to my limited reputation, cannot upvote your answer. Sorry about this. 囧 – Curtis2 Oct 12 '15 at 02:09
  • 1
    @Curtis2: Don't worry! It's more than enough for me to know that this helped you :)! – 3442 Oct 12 '15 at 02:10
  • Then what is the point of placement new, that is how is `new(ptr) X(...);` different from `ptr* = X(...);`? – Géry Ogam Sep 11 '21 at 23:14
0

The questions are: first, will the object pointed to by pc3 overwrite the one pointed to by pc1?"

This is dangerous, both objects will be attempting to share the same block of memory.

If yes, why we still possible to call the destructor explicitly(pc1->~JustTesting();) to free the object's memory.

This does not free the memory of an object created via placement new. It simply calls the destructor of the object. The conventional delete operator will call the destructor, then attempt to free the memory by assuming it was allocated normally. In this example, it will effectively try to call a destructor on that block of memory twice, due to the sharing issue that has already been raised.

question two: Does the order of these two explicit destructor call matter?

No, in either case, the second call is likely to have issues.

question three: What is the meaning of "the pointer pc3 does not receive an address returned by new"? Is the address return by new different with the address given by assignment?

The address returned by new is a block of freshly allocated memory from the heap (assuming the default new operator hasn't been overridden). The address returned by placement new is simply the address of the block of memory you gave to it.

gigaplex
  • 471
  • 2
  • 8
  • Thank you for your answer. But I still don't understand, other than call the destructor, why we cannot directly `delete` a placement-new-ed object. – Curtis2 Oct 12 '15 at 01:58
  • Because `delete` doesn't just call the destructor, it then uses the default allocator to free the memory. Since the memory isn't owned by the default allocator, it will lead to undefined behaviour, usually by crashing. It's typically equivalent to calling the destructor manually, followed by calling `free` on the pointer. – gigaplex Oct 12 '15 at 02:47
0

first, will the object pointed to by pc3 overwrite the one pointed to by pc1?

Yes. They will both be stored at the address pointed to by for buffer. This may be undefined behaviour, and is generally a bad idea anyway (unless you know for sure that the class's destructor doesn't do anything important).

If yes, why we still possible to call the destructor explicitly(pc1->~JustTesting();) to free the object's memory.

Most likely, pc1 and pc3 point to the same location. I'm not sure if that's guaranteed, but I can't imagine why it would not be the case on any implementation.

If pc1 and pc3 point to the same location, then this question is like asking why the following code works:

char *p1 = new char[50];
char *p2 = p1;
delete [] p2;

What is the meaning of "the pointer pc3 does not receive an address returned by new"? Is the address return by new different with the address given by assignment?

Bad wording probably. Clearly pc3 does receive an address returned by new - you can see it right there in the code:

pc3 = new // ... other stuff ...

I suspect they mean that the address was not allocated by new. Normally new allocates a block of memory to hold an object, and then constructs an object in that memory block. With placement new, it only constructs an object in the provided memory block.

Likewise, delete will destroy the object whose pointer was passed to it, and then free a memory block that was previously allocated at that address. deleteing an object not allocated with new is undefined behaviour - think of JustTesting a("Object on stack", 30); delete &a;.

If you're lucky, delete pc3 might have the same effect as pc3->~JustTesting(); delete [] buffer;, so it might not crash - although then delete [] buffer; would crash later since you're deleting an object twice. You definitely should not rely on that. The only safe thing to do is to not delete the objects allocated with placement new.

Community
  • 1
  • 1
user253751
  • 57,427
  • 7
  • 48
  • 90
0

The questions are: first, will the object pointed to by pc3 overwrite the one pointed to by pc1?

Yes.

If yes, why we still possible to call the destructor explicitly(pc1->~JustTesting();) to free the object's memory.

pc does not already exist, any function invoking on it is UB.

question two: Does the order of these two explicit destructor call matter?

You shouldn't call them together at all.

question three: What is the meaning of "the pointer pc3 does not receive an address returned by new"? Is the address returned by new different with the address given by assignment?

I think it should mean "the pointer pc3 does not receive an address returned by newly allocated". The placement new just return the address you passed to it.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405