When Should I Create a Destructor

When should I provide a destructor for my class?

You need to define a destructor if the default destruction does not suffice. Of course, this just punts the question: what does the default destructor do? Well, it calls the destructors of each of the member variables, and that's it. If this is enough for you, you're good to go. If it's not, then you need to write a destructor.

The most common example is the case of allocating a pointer with new. A pointer (to any type) is a primitive, and the destructor just makes the pointer itself go away, without touching the pointed to memory. So the default destructor of a pointer does not have the right behavior for us (it will leak memory), hence we need a delete call in the destructor. Imagine now we change the raw pointer to a smart pointer. When the smart pointer is destroyed, it also calls the destructor of whatever its pointing to, and then frees the memory. So a smart pointer's destructor is sufficient.

By understanding the underlying reason behind the most common case, you can reason about less common cases. It's true that very often, if you're using smart pointers and std library containers, their destructors do the right thing and you don't need to write a destructor at all. But there are still exceptions.

Suppose you have a Logger class. This logger class is smart though, it buffers up a bunch of messages to Log, and then writes them out to a file only when the buffer reaches a certain size (it "flushes" the buffer). This can be more performant than just dumping everything to a file immediately. When the Logger is destroyed, you need to flush everything from the buffer regardless of whether it's full, so you'll probably want to write a destructor for it, even though its easy enough to implement Logger in terms of std::vector and std::string so that nothing leaks when its destroyed.

Edit: I didn't see question 2. The answer to question 2 is that you should not call delete if it is a non-owning pointer. In other words, if some other class or scope is solely responsible for cleaning up after this object, and you have the pointer "just to look", then do not call delete. The reason why is if you call delete and somebody else owns it, the pointer gets delete called on it twice:

struct A {
A(SomeObj * obj) : m_obj(obj){};
SomeObj * m_obj;
~A(){delete m_obj;};
}

SomeObj * obj = new SomeObj();
A a(obj);
delete obj; // bad!

In fact, arguably the guideline in c++11 is to NEVER call delete on a pointer. Why? Well, if you call delete on a pointer, it means you own it. And if you own it, there's no reason not to use a smart pointer, in particular unique_ptr is virtually the same speed and does this automatically, and is far more likely to be thread safe.

Further, furthermore (forgive me I'm getting really into this now), it's generally a bad idea to make non-owning views of objects (raw pointers or references) members of other objects. Why? Because, the object with the raw pointer may not have to worry about destroying the other object since it doesn't own it, but it has no way of knowing when it will be destroyed. The pointed to object could be destroyed while the object with the pointer is still alive:

struct A {
SomeObj * m_obj;
void func(){m_obj->doStuff();};
}

A a;
if(blah) {
SomeObj b;
a.m_obj = &b;
}
a.func() // bad!

Note that this only applies to member fields of objects. Passing a view of an object into a function (member or not) is safe, because the function is called in the enclosing scope of the object itself, so this is not an issue.

The harsh conclusion of all this is that unless you know what you're doing, you just shouldn't ever have raw pointers or references as member fields of objects.

Edit 2: I guess the overall conclusion (which is really nice!) is that in general, your classes should be written in such a way that they don't need destructors unless the destructors do something semantically meaningful. In my Logger example, the Logger has to be flushed, something important has to happen before destruction. You should not write (generally) classes that need to do trivial clean-up after their members, member variables should clean up after themselves.

When do I need to declare my own destructor?

Introduction

Since data-members with automatic-storage duration have their lifetime bound to that of the instance having them, there's no need for you to call their destructor explicitly; they will always be destroyed whenever the instance of the class is.


When do I normally need to declare my own destructor?

Normally you will have to explicitly declare your own destructor if:

  • You are declaring a class which is supposed to serve as a base for inheritance involving polymorphism, if you do you'll need a virtual destructor to make sure that the destructor of a Derived class is called upon destroying it through a pointer/reference to Base.

  • You need to release resourced aquired by the class during its leftime

    • Example 1: The class has a handle to a file, this needs to be closed when the object destructs; the destructor is the perfect location.

    • Exempel 2: The class owns an object with dynamic-storage duration, since the lifetime of the object can potentially live on long after the class instance has been destroyed you'll need to explicitly destroy it in the destructor.


The implicitly generated destructor

Unless you explicitly declare your own destructor, an implicitly generated destructor will be created for you by the compiler.

14.4p4 Destructors [class.dtor]

If a class has no user-declared destructor, a destructor is
implicitly declares as defaulted (8.4). An implicitly declared
destructor is an inline public member of its class.


src: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf


There are however some rare/advanced cases where the compiler will refuse to generate a destructor for you, under these cases you must explicitly declare your own, or make sure that the destructor for your class instance is never called; because in order to destroy an object, a destructor is neccessary.

14.4p5 Destructors [class.dtor]

A default destructor for a class X is defined as delete if:


  • X is a union-like class that has a variant member with a non-trivial
    destructor,

  • any of the non-static data members has class type M (or array
    thereof) and M has a deleted destructor or a destructor that is
    inaccessible from the default destructor,

  • any direct or virtual base class has a deleted destructor or a
    destructor that is inaccessible from the default destructor,

  • or, for a virtual destructor, lookup of the non-array deallocation
    function results in an ambiguity or in a function that is deleted
    or inaccessible from the default destructor.


src: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf


More can be read under the following link:

  • cppreference.com - Destructors

When do we need to define destructors?

The rule of Three and The Rule of Zero

The good ol' way of handling resources was with the Rule of Three (now Rule of Five due to move semantic), but recently another rule is taking over: the Rule of Zero.

The idea, but you should really read the article, is that resource management should be left to other specific classes.

On this regard the standard library provides a nice set of tools like: std::vector, std::string, std::unique_ptr and std::shared_ptr, effectively removing the need for custom destructors, move/copy constructors, move/copy assignment and default constructors.

How to apply it to your code

In your code you have a lot of different resources, and this makes for a great example.

The string

If you notice brandname is effectively a "dynamic string", the standard library not only saves you from C-style string, but automatically manages the memory of the string with std::string.

The dynamically allocated B

The second resource appears to be a dynamically allocated B. If you are dynamically allocating for other reasons other than "I want an optional member" you should definitely use std::unique_ptr that will take care of the resource (deallocating when appropriate) automatically. On the other hand, if you want it to be an optional member you can use std::optional instead.

The collection of Bs

The last resource is just an array of Bs. That is easily managed with an std::vector. The standard library allows you to choose from a variety of different containers for your different needs; Just to mention some of them: std::deque, std::list and std::array.

Conclusion

To add all the suggestions up, you would end up with:

class A {
private:
std::string brandname;
std::unique_ptr<B> b;
std::vector<B> vec;
public:
virtual void something(){} = 0;
};

Which is both safe and readable.

When to use virtual destructors?

Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:

class Base 
{
// some virtual methods
};

class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};

Here, you'll notice that I didn't declare Base's destructor to be virtual. Now, let's have a look at the following snippet:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Since Base's destructor is not virtual and b is a Base* pointing to a Derived object, delete b has undefined behaviour:

[In delete b], if the static type of the
object to be deleted is different from its dynamic type, the static
type shall be a base class of the dynamic type of the object to be
deleted and the static type shall have a virtual destructor or the
behavior is undefined
.

In most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.

To sum up, always make base classes' destructors virtual when they're meant to be manipulated polymorphically.

If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete on a base class pointer.

You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.

Destructor in struct and class in c++

Whether you need a destructor is NOT determined by whether you use a struct or class. The deciding factor is whether the struct/class has acquired resources that must be released explicitly when the life of the object ends. If the answer to the question is yes, then you need to implement a destructor. Otherwise, you don't need to implement it.

For the first set of structs, it's best to let the compiler implement the destructors. There is nothing to do in the destructors.

If you implement them, they will be:

~mySubject() {}

and

~myLogStructure() {}

You may need to implement destructors in the second set of classes depending on whether you need to release any resources.

Since you have pointers to w_char to store that data, you most likely need to implement destructors, and release the memory acquired during the life time of the object.

When should you not use virtual destructors?

There is no need to use a virtual destructor when any of the below is true:

  • No intention to derive classes from it
  • No instantiation on the heap
  • No intention to store with access via a pointer to a superclass

No specific reason to avoid it unless you are really so pressed for memory.



Related Topics



Leave a reply



Submit