Any reasons to use a a method instead of the class own destructor to clean up in C++?

StackOverflow https://stackoverflow.com/questions/22357722

  •  13-06-2023
  •  | 
  •  

Question

Recently I am following the tutorials on rastertek and find that they suggest use a Shutdown() method for cleaning up instead of the class own destructor.The reason they mention is that the destructor is not guaranteed to be executed when calling some unsafe function like ExitThread().

However, I doubt if that method would get executed when even the destructor cannot be called. Indeed you can always call Shutdown() before you call ExitThread() but why not the same for the destructor? If I can do something before calling ExitThread(), I can certainly call the destructor as well.

Isn't placing the clean up code in the destructor more or less safer than using another method to do the trick? I know that releasing some vital resources like closing a file may need this separate method to do the trick. But are there any reasons other than that since this does not seem to be the case in the tutorials?

For the record, I know there is a similar question out there. But that one got no answer.

Was it helpful?

Solution 2

The only advantage to moving initialization out of the constructor, and for removing cleanup out of the destructor is when you've got a base class framework where you want to reliably call virtual methods during these stages.

Since the vtable is changing during construction/destruction calls to virtual functions don't resolve to the most derived instance. By having explicit Initialize/Shutdown methods you can be sure the virtual functions dispatch correctly.

Please note, this isn't an answer that advocates this approach, just one that is trying to work out why they've suggested it!

OTHER TIPS

Isn't placing the clean up code in the destructor more or less safer than using another method to do the trick?

The problem here is that while ExitThread (and other functions like it) is a perfect API for C, with C++ code, it breaks stack unwinding.

The correct solution for C++ is to make sure you do not call ExitThread (and such) in code using anything with destructors.

Problem:

void thread_function()
{
    raii_resource r { acquire_resource() };
    ExitThread();
    // problem: r.~raii_resource() not called
}

The shutdown solution:

void thread_function()
{
    raii_resource r { acquire_resource() };
    r.shutdown(); // release resources here
    ExitThread();
    // r.~raii_resource() still not called
}

The shutdown solution is not obvious at all in client code. As @stefan said, kill it with fire.

Better solution (than the Shutdown thing):

void thread_function()
{
    { // artificial scope where RAII objects live
        raii_resource r { acquire_resource() };
    }
    // this space does not support RAII life
    ExitThread();
}

RAII works fine here, but the artificial scope is not very elegant. On top, it's as inelegant as the shutdown solution (it requires a non-obvious artifice in client code).

Better (cleaner) solution:

template<typename F>
void run_thread(F functor)
{
    functor(); // all RAII resources inside functor; this is simple and 
               // obvious from client code
    ExitThread();
}

destructors are guaranteed to be called when an object is destroyed however thread clean up can require a tad more than just object destruction. Generally speaking cleanup methods are added when you need to handle releasing of shared resources, dealing with ancient libraries, etc.

Specifically you're dealing with the Win32 API which qualifies as an ancient c-style library considering ExitThread has been around longer than I have ...

With such approach you'll need to call Shutdown in all cases where the object should be destroyed - each time when it leaves the scope. In the case of exceptions (if they are used) you'll cannot call it. Destructor will be called automatically in these cases.

"I can certainly call the destructor as well" - calling the destructor explicitly is highly not recommended because it will be called automatically in any case. This should be avoided except only in special cases. If you mean this code from the tutorial:

System->Shutdown();
delete System;

then I don't see the difference because delete System; will call the destructor anyway.

In any case I would prefer

{
    // work with System
    ...
}

mentioned in @utnapistim answer. I do not see any minuses in such way of coding, and also it is common way to specify scope. Even not going deep in the details of legacy ::ExitThread you gain auto cleanup. It is possible to work with WinApi with C++ RAII technique, look for instance at my code: multithreading sample project. Initial commit was bare WinAPI, and next commit introduced resource wrappers. You can compare both variants - second is much clearer, imho.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top