0

When is it essential that a class have to define a destructor in C++ class and why?

Just starting to learn this in C++ and need it to be clarified.

RiaD
  • 46,822
  • 11
  • 79
  • 123
Joe
  • 9
  • 1
  • Possible duplicate of [What is The Rule of Three?](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). Not an exact duplicate, but anybody asking this question will learn what they should know by learning the Rule of Three. – Nemo Jul 15 '13 at 01:53
  • I don't see this as a bad question, certainly a beginners question, but it isn't a subjective question at least – aaronman Jul 18 '13 at 01:22

6 Answers6

5

You only need to define a destructor if your class needs to do special cleanup, such as freeing dynamically allocated memory.

samoz
  • 56,849
  • 55
  • 141
  • 195
3

You need to define a destructor when you have dynamically allocated memory. A good rule of thumb is if you use new in any of your constructors you probably need a destructor. Anything that isn't automatic storage or static storage is considered dynamically allocated

Anything that would fall under the category of cleaning up is appropriate to put in a destructor for example closing a network connection

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • 2
    That's the most frequent case, but by far not the only. You might also want in the destructor to e.g. close a network connection your class manages. – celtschk Jul 15 '13 at 00:51
  • @celtschk for beginners I think this is good enough, I don't think joe needs to worry about network connections yet – aaronman Jul 15 '13 at 00:54
  • @celtschk also I said need, while it might be appropriate to close a connection in the destructor you could also have a close method that you call before deleting it – aaronman Jul 15 '13 at 00:56
  • "The *only* time" is wrong and misleading. There's no need to go into detail with other cases, but it is *wrong* to claim they don't exist. – celtschk Jul 15 '13 at 00:56
  • Even if you have a special close method, you *still* want to close it in the destructor. For example, if the destructor is called due to an exception, you most likely didn't call the close method. – celtschk Jul 15 '13 at 00:56
  • @celtschk I think that is a matter of style, not that I wouldn't put it in the destructor – aaronman Jul 15 '13 at 00:58
  • @celtschk I fixed the answer to make it less misleading – aaronman Jul 15 '13 at 00:59
  • Thanks, now you've gotten a +1 from me. (Although you might want to expand "automatic storage" to "automatic or static storage") – celtschk Jul 15 '13 at 01:00
1

There might be other reasons, but the first one i'm thinking of is that some of your class attributes might need to be explicitely freed.

if your class simply have, say, two int attributes, then those will be deleted along with your object automatically when you call delete myObject. But if it includes any dynamically allocated attribute, those won't be freed along with your object, so you'll need to explicitely delete them in your destructor.

astrognocci
  • 1,057
  • 7
  • 16
1

You only have to define a destructor if the default behavior of the destructor is not what you want.

When an object is destroyed, the destructor does these things, whether you define a custom destructor or not:

  • calls the destructor of each data member.
  • calls the destructor of each base.

This actually handles most things that you might want to do. It used to be common to define a custom destructor when memory was allocated in the constructor:

struct A {
    B *b_ptr;

    A() : b_ptr(new B) { }
    ~A() { delete b_ptr; }
};

But when you are using standard containers and smart pointers, the number of cases where you need a custom destructor are relatively small. For example, if you have a class like this

struct A {
    std::unique_ptr<B> b_ptr;

    A() : b_ptr(new B) { }
};

there is no need to define your own destructor, since the std::unique_ptr will free the allocated memory. Likewise, there is no need to define a destructor here

struct B {
    ifstream input_stream;

    B(const std::string &path) : input_stream(path) { }
};

since the input_stream will automatically be closed when the input_stream member is destroyed.

From time to time, there are cases where you need to do something special though. You need a special call to acquire or release a resource. In that case, you want to have a destructor:

struct C {
    ResourceHandle resource_handle;
    C() : resource_handle(resouce_manager.acquireResource()) { }
    ~C() { resource_manager.releaseResource(resource_handle); }
};

But as much as possible, you want to localize this kind of behavior so that it doesn't have to be duplicated in many classes. For example, you might want to make class like this:

struct ResourceHolder {
    ResourceHandle resource_handle;
    ResourceHolder() : resource_handle(acquireResource()) { }
    ~ResourceHolder() { releaseResource(resource_handle); }
};

Now you can just use a ResourceHolder within each class that needs one of these resources, and you never have to repeat the acquire and release logic again.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
0

A destructor is not only used to free memory allocated, for instance, in the constructor.

You can achieve RAII-style functionality by using a destructor.

Example of types that take advantage of this:

std::fstream closes opened files

std::lock_guard unlocks a mutex

std::shared_ptr decrements a reference counter or free the memory if the counter is 0.

a.lasram
  • 4,371
  • 1
  • 16
  • 24
0

There are 2 instances that you need to define a destructor:

  1. You are allocating resources somewhere in your class, also consider copy constructor and assignment operator, you will likely need to define them even if it is to prevent them being used.
  2. The class will be derived from. In this case the destructor must be virtual. The reason is that if the derived class will be destructed using a reference or pointer to the parent class which holds an instance of the derived class the destructor of the derived class won't be called.
Dominique McDonnell
  • 2,510
  • 16
  • 25