I thought I'd chime in starting with this point:
it is really unusual to call the destructor manually.
|
While true, it is far from a problem by itself. It is a technique used for special storage circumstances.
The example code is not that case, however.
It is used, for example, in the std::shared_ptr collection of classes (there are nodes inside you don't generally see). More specifically, the "std::make_shared" method for creating instances for the shared_ptr storage uses both in place construction and explicit destructor calls in order to provide custom allocation for the user's instance.
I deliberately added something that should have cause a run-time error...
I press enter but it stays frozen.
|
While @jib's point the undefined behavior doesn't always mean a crash, this situation is predictable.
The destructor makes not use or reference to member information. Further, the class itself defines no data members. As such, there's nothing that could cause a crash.
Factually, there's nothing dangerous to be expected in this example, and it actually does work, though it is ill advised.
First, the explicit call of the destructor functions like any other function call, in that there is no inherent de-allocation of storage. The destructor function is typically associated with de-allocation, but it does not perform de-allocation, it is a function to be called
before de-allocation.
Since the destructor does nothing to the instance, it will operate any number of times without issue. This is a fragile situation, though, because if the destructor had performed other work on the instance, say de-allocating some dynamically allocated object held via smart pointer, or a string (same thing, really), then you'd expect a crash due to performing those actions twice.
The reason the software seems to hang is the use of "cin" in the destructor. Depending on the operating system one must enter a kind of termination, or some kind of string information...merely hitting return on many operating systems merely "repeats" the cin as if it had done nothing (ignored the enter). When I entered a ctrl-Z on Windows, for example, I got the remaining output expected from the subsequent double destructor call, and the program otherwise terminated normally.
In this example there is only 1 deallocation of Sally (which does not happen at the explicit destructor call). Since there are no data members, and no action taken on the instance of Sally, the call to so.printStuff is still operating on an instance (as allocated) which has not be changed from before the explicit destructor was called. We should expect this will continue to operate, but as I pointed out, it is a fragile scenario and certainly not something one should write.
The explicit call to the destructor should be reserved for custom allocation situations. For example, if a raw block of RAM were held by a void * (or char *), and the object were positioned upon some block within that memory, where in-place construction were used to initialize the object (a kind of explicit call of a constructor where the object is already allocated using "in place new"), then and only then is it appropriate to call the destructor explicitly, and then only because that storage would presumably be returned for availability within the custom memory system represented by that block of RAM.
There is no correct way to use the explicit destructor call for an object instantiated on the stack as Sally is done here, because there's no way to suppress the destructor from being called when that falls from scope.