There is no need for the allocating new here. You just need to make sure that you have an array of sufficient size and alignment:
alignas(display) std::byte memory[sizeof(display)];
display *obj = nullptr;
(Instead of std::byte you can use unsigned char, but I think std::byte, which is available since C++17, expresses the intent as raw memory storage better.)
Then construct the object with
obj = new(memory) display(a);
and when it is not needed anymore, call its destructor explicitly:
obj->~display();
No delete is needed in this case. With your approach an additional delete[] memory; after the destructor call would be required to free the memory allocated with the first new, if you don't intent to reuse it after the destructor call (which you can do e.g. in a loop constructing a new display with placement-new). Note that you need to call the destructor on obj and the delete[] on memory. This is not interchangeable. memory is a pointer to the allocated memory block and obj a pointer to the object nested in it. The former was allocated with allocating new[], so required delete[], and the latter was only created with the (non-allocating) placement-new, so requires only an explicit destructor call.
Of course, you can consider whether the destructor call is really needed. If the display doesn't hold any resources that need to be cleaned up, then you can skip it, although I would be safe and call it anyway, just in case display will be changed later.
Also, the standard library since C++17 implements all of this as std::optional. If you can use it, then do so:
std::optional<display> obj;
// obj is now empty, can be tested with `if(obj)`
obj.emplace(/* constructor arguments */);
// obj now contains a display that can be accessed like a pointer with * and ->
// destructor of obj will take care of correctly destroying the display
emplace can also be called multiple times to replace the display with a new one (and calling the old one's destructor) or .reset() can be used to explicitly empty the optional.
If you don't have C++17 available, a std::unique_ptr can be used in a similar way, except that it will use a heap allcoation, which std::optional doesn't, and that std::unique_ptr isn't copyable even if display is, while std::optional will be.
std::optional<display> obj;
// obj is now empty, can be tested with `if(obj)`
obj = std::make_unique<display>(/* constructor arguments */);
// obj now contains a display that can be accessed like a pointer with * and ->
// destructor of obj will take care of correctly destroying the display
obj can be reassigned in that way multiple times as well or reset with = nullptr; or .reset() and in either case it will take care of correctly destroying any display just like std::optional does.