Is calling destructor manually always a sign of bad design?
Is calling destructor manually always a sign of bad design?
I was thinking: they say if you're calling destructor manually - you're doing something wrong. But is it always the case? Are there any counter-examples? Situations where it is neccessary to call it manually or where it is hard/impossible/impractical to avoid it?
@peachykeen: you would call placement
new to initialize a new object in place of the old. Generally not a good idea, but it isn't unheard of.– D.Shawley
Jan 6 '13 at 21:38
new
Look at "rules" that contain the words "always" and "never" that don't come directly from specifications with suspect: in the most of the cases who is teaching them wants to hide you things you should know but he doesn't know how to teach. Just like an adult answering to a child to a question about sex.
– Emilio Garavaglia
Jan 6 '13 at 22:24
13 Answers
13
Calling the destructor manually is required if the object was constructed using an overloaded form of operator new(), except when using the "std::nothrow" overloads:
operator new()
std::nothrow
T* t0 = new(std::nothrow) T();
delete t0; // OK: std::nothrow overload
void* buffer = malloc(sizeof(T));
T* t1 = new(buffer) T();
t1->~T(); // required: delete t1 would be wrong
free(buffer);
Outside managing memory on a rather low level as above calling destructors explicitly, however, is a sign of bad design. Probably, it is actually not just bad design but outright wrong (yes, using an explicit destructor followed by a copy constructor call in the assignment operator is a bad design and likely to be wrong).
With C++ 2011 there is another reason to use explicit destructor calls: When using generalized unions, it is necessary to explicitly destroy the current object and create a new object using placement new when changing the type of the represented object. Also, when the union is destroyed, it is necessary to explicitly call the destructor of the current object if it requires destruction.
Instead of saying "using an overloaded form of
operator new", the correct phrase is "using placement new".– Remy Lebeau
Jan 6 '13 at 22:47
operator new
placement new
@RemyLebeau: Well, I wanted to clarify that I'm not only talking only of
operator new(std::size_t, void*) (and the array variation) but rather about all overloaded version of operator new().– Dietmar Kühl
Jan 6 '13 at 22:56
operator new(std::size_t, void*)
operator new()
What about when you want to copy an object to do an operation in it without altering it while the operation is computing?
temp = Class(object); temp.operation(); object.~Class(); object = Class(temp); temp.~Class();– Jean-Luc Nacif Coelho
Feb 23 '17 at 2:20
temp = Class(object); temp.operation(); object.~Class(); object = Class(temp); temp.~Class();
yes, using an explicit destructor followed by a copy constructor call in the assignment operator is a bad design and likely to be wrong. Why do you say this? I would think that if the destructor is trivial, or close to trivial, it has minimal overhead and increases the use of the DRY principle. If used in such cases with a move operator=(), it may even be better than using swap. YMMV.– Adrian
Apr 3 at 15:07
yes, using an explicit destructor followed by a copy constructor call in the assignment operator is a bad design and likely to be wrong
operator=()
@Adrian: calling the destructor and recreating the object very easily changes the type of the object: it will recreate an object with the static type of the assignment but the dynamic type may be different. That is actually an issue when the class has
virtual functions (the virtual functions won’t be recreated) and otherwise the object is just partially [re-]constructed.– Dietmar Kühl
Apr 3 at 21:19
virtual
virtual
All answers describe specific cases, but there is a general answer:
You call the dtor explicitly every time you need to just destroy the object (in C++ sense) without releasing the memory the object resides in.
This typically happens in all the situation where memory allocation / deallocation is managed independently from object construction / destruction. In those cases construction happens via placement new upon an existent chunk of memory, and destruction happens via explicit dtor call.
Here is the raw example:
{
char buffer[sizeof(MyClass)];
{
MyClass* p = new(buffer)MyClass;
p->dosomething();
p->~MyClass();
}
{
MyClass* p = new(buffer)MyClass;
p->dosomething();
p->~MyClass();
}
}
Another notable example is the default std::allocator when used by std::vector: elements are constructed in vector during push_back, but the memory is allocated in chunks, so it pre-exist the element contruction. And hence, vector::erase must destroy the elements, but not necessarily it deallocates the memory (especially if new push_back have to happen soon...).
std::allocator
std::vector
vector
push_back
vector::erase
It is "bad design" in strict OOP sense (you should manage objects, not memory: the fact objects require memory is an "incident"), it is "good design" in "low level programming", or in cases where memory is not taken from the "free store" the default operator new buys in.
operator new
It is bad design if it happens randomly around the code, it is good design if it happens locally to classes specifically designed for that purpose.
Just curious as to why this isn't the accepted answer.
– Francis Cugler
Mar 13 at 10:18
As quoted by the FAQ, you should call the destructor explicitly when using placement new.
This is about the only time you ever explicitly call a destructor.
I agree though that this is seldom needed.
No, Depends on the situation, sometimes it is legitimate and good design.
To understand why and when you need to call destructors explicitly, let's look at what happening with "new" and "delete".
To created an object dynamically, T* t = new T; under the hood: 1. sizeof(T) memory is allocated. 2. T's constructor is called to initialize the allocated memory. The operator new does two things: allocation and initialization.
T* t = new T;
To destroy the object delete t; under the hood: 1. T's destructor is called. 2. memory allocated for that object is released. the operator delete also does two things: destruction and deallocation.
delete t;
One writes the constructor to do initialization, and destructor to do destruction. When you explicitly call the destructor, only the destruction is done, but not the deallocation.
A legitimate use of explicitly calling destructor, therefore, could be, "I only want to destruct the object, but I don't (or can't) release the memory allocation (yet)."
A common example of this, is pre-allocating memory for a pool of certain objects which otherwise have to be allocated dynamically.
When creating a new object, you get the chunk of memory from the pre-allocated pool and do a "placement new". After done with the object, you may want to explicitly call the destructor to finish the cleanup work, if any. But you won't actually deallocate the memory, as the operator delete would have done. Instead, you return the chunk to the pool for reuse.
No, you shouldn't call it explicitly because it would be called twice. Once for the manual call and another time when the scope in which the object is declared ends.
Eg.
{
Class c;
c.~Class();
}
If you really need to perform the same operations you should have a separate method.
There is a specific situation in which you may want to call a destructor on a dynamically allocated object with a placement new but it doesn't sound something you will ever need.
new
Any time you need to separate allocation from initialization,
you'll need placement new and explicit calling of the destructor
manually. Today, it's rarely necessary, since we have the
standard containers, but if you have to implement some new sort
of container, you'll need it.
There are cases when they are necessary:
In code I work on I use explicit destructor call in allocators, I have implementation of simple allocator that uses placement new to return memory blocks to stl containers. In destroy I have:
void destroy (pointer p) {
// destroy objects by calling their destructor
p->~T();
}
while in construct:
void construct (pointer p, const T& value) {
// initialize memory with placement new
#undef new
::new((PVOID)p) T(value);
}
there is also allocation being done in allocate() and memory deallocation in deallocate(), using platform specific alloc and dealloc mechanisms. This allocator was used to bypass doug lea malloc and use directly for example LocalAlloc on windows.
What about this?
Destructor is not called if an exception is thrown from the constructor, so I have to call it manually to destroy handles that have been created in the constructor before the exception.
class MyClass {
HANDLE h1,h2;
public:
MyClass() {
// handles have to be created first
h1=SomeAPIToCreateA();
h2=SomeAPIToCreateB();
...
try {
if(error) {
throw MyException();
}
}
catch(...) {
this->~MyClass();
throw;
}
}
~MyClass() {
SomeAPIToDestroyA(h1);
SomeAPIToDestroyB(h2);
}
};
This seems questionable: when your constructor trows, you don't know (or may not know) which parts of the object had been constructed and which hadn't. So you don't know for which sub-objects to call destructors, for instance. Or which of the resources allocated by the constructor to deallocate.
– Violet Giraffe
Jun 30 at 8:13
@VioletGiraffe if the sub-objects are constructed on stack, i.e. not with "new", they will be destroyed automatically. Otherwise you can check if they are NULL before destroying them in the destructor. Same with the resources
– CITBL
Jul 1 at 16:50
I have never come across a situation where one needs to call a destructor manually. I seem to remember even Stroustrup claims it is bad practice.
You've never written a memory pool...
– Luchian Grigore
Jan 6 '13 at 21:41
You are correct. But I have used a placement new. I was able to add the cleanup functionality in a method other then the destructor. The destructor is there so it can "automatically" be called when one does delete, when you manually want to destruct but not deallocate you could simply write an "onDestruct" couldn't you? I would be interested to hear if there are examples where an object would have to do its destruction in the destructor because sometimes you would need to delete and other times you would only want to destruct and not deallocate..
– Lieuwe
Jan 7 '13 at 9:09
And even in that case you could call onDestruct() from within the destructor - so I still don't see a case for manually calling the destructor.
– Lieuwe
Jan 7 '13 at 9:26
Who is Strousup?
– Jim Balter
Jun 6 '13 at 11:24
@JimBalter: creator of
C+ ☺– Mark K Cowan
Sep 14 '16 at 14:13
C+
I found 3 occasions where I needed to do this:
Found another example where you would have to call destructor(s) manually. Suppose you have implemented a variant-like class that holds one of several types of data:
struct Variant {
union {
std::string str;
int num;
bool b;
};
enum Type { Str, Int, Bool } type;
};
If the Variant instance was holding a std::string, and now you're assigning a different type to the union, you must destruct the std::string first. The compiler will not do that automatically.
Variant
std::string
std::string
Memory is no different than other resource: you should have a look at http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style especially the part where Bjarne talks about RAII (around ~30min)
All the necessary templates (shared_ptr, unique_ptr, weak_ptr) are part of the C++11 standard library
I have another situation where I think it is perfectly reasonable to call the destructor.
When writing a "Reset" type of method to restore an object to its initial state, it is perfectly reasonable to call the Destructor to delete the old data that is being reset.
class Widget
{
private:
char* pDataText { NULL };
int idNumber { 0 };
public:
void Setup() { pDataText = new char[100]; }
~Widget() { delete pDataText; }
void Reset()
{
Widget blankWidget;
this->~Widget(); // Manually delete the current object using the dtor
*this = blankObject; // Copy a blank object to the this-object.
}
};
Wouldn't it look cleaner if you declared a special
cleanup() method to be called in this case and in the destructor?– Violet Giraffe
May 25 '16 at 14:19
cleanup()
A "special" method that is only called in two cases? Sure... that sounds totally correct (/sarcasm). Methods should be generalized and able to be called anywhere. When you want to delete an object, there is nothing wrong with calling its destructor.
– abelenky
May 25 '16 at 14:44
You must not call the destructor explicitly in this situation. You'd have to implement an assignment operator, anyway.
– Rémi
Jul 13 '17 at 13:38
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
How are you going to deallocate the object after calling the dtor, without calling it again?
– ssube
Jan 6 '13 at 21:36