2

I have a C++ object that's being created in a plugin in a memory buffer provided by the host application with the placement new operator in a manner similar to the following code:

MyClass* createObject(void* inNewBlock)
{
  MyClass* elementAddr = static_cast<MyClass*>(inNewBlock);
  new (elementAddr) MyClass(); // Placement new into pre-allocated memory
}

I know that I can't delete an object created this way, but I wondered if there is a way to null out the memory and reallocate the object at a later point if I need to like so:

void removeObject(MyClass* object)
  memset(object, NULL, sizeof(MyClass));
}

void restoreObject(MyClass* object)
{
  new (object) MyClass(); // Placement new into pre-allocated memory
}

The code above doesn't work. I've tried it, and the host application hangs or crashes when I call restoreObject(). I was hoping someone could explain to me why this doesn't work and what an alternative might be.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
Greg K
  • 41
  • 1
  • 4

4 Answers4

7

This is too convoluted. You can simply destroy the object when you don't need it any more. Also, the placement-new argument is already a void pointer, no need to cast. This is very similar to what the standard library allocators do:

MyClass * create(void * addr)
{
  return ::new (addr) MyClass;    // default-initialize
}
MyClass * create(void * addr, const MyClass & x)
{
  return ::new (addr) MyClass(x); // copy-construct from prototype
}

void destroy(MyClass * p)
{
  p->~MyClass();   // you can now reuse the memory
}

There's no such thing as "restore"; you can create a new object in the memory that was erstwhile used by another object if you've properly destroyed that object.

Edit: I added another construction method that makes a copy of a given object, in case your class isn't default-constructible.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
4

You need to call the destructor on the object before setting that memory to 0. For example:

void removeObject(MyClass* object)
{
  object->~MyObject();
  memset(object, NULL, sizeof(MyClass);
}

Otherwise the object won't release its resources.

Apart from that, there's nothing inherently wrong with what you're doing. If it crashes after modifying it in the aforementioned way, it's the host application storing something that isn't compatible with every object and trying to use it with a different one. You'll have to show how it's being used in the host app.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
2

If you use placement new, you must call the destructor directly:

MyClass* elementAddr = new (inNewBlock) MyClass();
//Do stuff
elementAddr->~MyClass();
//inNewBlock is now free to be used for other purposes.
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

I strongly urge you to pass the size of the memory buffer, as a safeguard to prevent against bad things happening.

Also be aware of a big pitfall with placement new: memory alignment. See placement new + array +alignment.

Passing in an extra-large buffer can help.

MyClass* createObject(void* inNewBlock, size_t size)
{
  // basic sanity check
  assert(size >= sizeof(MyClass);

  MyClass* result = new (inNewBlock) MyClass(); 

  // result might not be the same as elementAddr
  // check that the result fits in-to the space provided.       
  assert(result + sizeof(MyClass) <= (char*)inNewblock + size);
  return result;
}

I know that I can't delete an object created this way

Actually you can (as shown in other answers):

void deleteObject(MyClass* c) 
{
   c->~MyClass();
}

MyClass* restoreObject(MyClass* c)
{
   return createObject(c, sizeof(MyClass));
}
Community
  • 1
  • 1
cdiggins
  • 17,602
  • 7
  • 105
  • 102