4

In Rastertek DirectX tutorials they have empty constructors and destructors and instead use initialize() and shutdown() functions for objects initialization and cleaning up. After using this design for a while I can somewhat understand the benefits of having an initialize() method, but I can't see how using a shutdown() method is any better than putting all the clean-up code in the destructor.

The reason they provide is the following:

You will also notice I don't do any object clean up in the class destructor. I instead do all my object clean up in the Shutdown function you will see further down. The reason being is that I don't trust it to be called. Certain windows functions like ExitThread() are known for not calling your class destructors resulting in memory leaks. You can of course call safer versions of these functions now but I'm just being careful when programming on windows.

So the general usage pattern looks like this:

class Renderer
{
public:
    Renderer() { }
    ~Renderer() { }

    bool initialize(...) { /* perform initialization */ }
    void shutdown() { /* clean-up */ }
}; 

Renderer* renderer = new Renderer;
renderer->initialize(...);

// use the renderer

renderer->shutdown();
delete renderer;
renderer = NULL;

When looking at Rastertek's code it seems to me that they're coming from C background (initializing all variables at the top of the function, using only raw pointers and raw arrays etc.), so I wonder if this is yet another thing that is unnecessary in modern C++ (for one it makes it more difficult to use smart pointers). Is there any real benefit to this design?

jaho
  • 4,852
  • 6
  • 40
  • 66
  • 1
    That argument doesn't apply at all. If `ExitThread` (or `std::exit`, or whatever other function that exits) is called, `shutdown` definitely won't be called. The way objects are created with `new` is completely unnecessary as well. If you don't know much about C++ and its style, I'd suggest not learning like that. If you do, just apply what you know in place of that. – chris Feb 01 '14 at 23:28
  • @chris i think the argument is that if you create an object on the stack in say `main` somewhere, and hope the destructor is called, you may be disappointed. There's no way to call the destructor yourself (in this case) unless you have a `shutdown` function. Of course, it's not a great argument, and it is only works when you are the one destroying your objects. – user3125280 Feb 01 '14 at 23:43
  • @user3125280, Sure it's possible: `obj.~Class();`. I'm not saying that's a particularly good thing to do, but if you're about to exit without it being called, it's *possible*. – chris Feb 01 '14 at 23:48
  • You would want to separate your `shutdown` code if that code could throw an exception, in which case the semantics of a destructor is really "abandon" rather than "clean up". – spraff Feb 02 '14 at 00:07
  • Even so, the destructor could swallow exceptions while `shutdown` doesn't. At least give a way to automatically clean up the best it can. – chris Feb 02 '14 at 00:09
  • @chris http://www.parashift.com/c++-faq/dont-call-dtor-on-local.html - the premise of the argument is that there is no way to be 100% sure that the destructor is or isn't called. – user3125280 Feb 02 '14 at 04:38
  • @user3125280, I didn't say it was a good idea, but it conforms to the FAQ completely. You call it explicitly right before calling whatever function will skip calling the destructors. The behaviour is 100% predictable. – chris Feb 02 '14 at 04:59
  • @chris the problem shutdown is supposed to solve is that the programmer doesn't know if the (automatic call of the) destructor will be skipped. `int main() { Class a; }` - possibly a is deleted, possibly not (in the case of ExitThread) compare to `int main() {Class a; a.~Class;}` (destructor may be called twice) and `int main() { Class a; a.shutdown(); a.~Class(); }` (problem solved - the destructor call is optional and a double call is not UB for a trivial destructor) – user3125280 Feb 02 '14 at 06:09
  • To add to the question, I've now been looking at Crytek's CryEngine 3 code and they use exactly the same approach. Not only that, but they also call `this.~Game()` at the end of the `shutdown()` method. I still can't understand the benefits of this pattern. Btw. they don't use exceptions in the code. It seems like it's a somewhat of a common approach in games development. Anyone here who could describe the reason behind it? – jaho Feb 12 '14 at 18:58

1 Answers1

1

Generally, it is a bad advice not to do a cleanup in the destructor.

But you can do it if the clean operation can fail, and you want to throw an exception. Then you have to be careful, as another exception would call abort(). For that particular case, doing cleanup in a separate function makes sense.

By the way, the code example indeed looks like from someone coming from the c world.

The reason being is that I don't trust it to be called. Certain windows functions like ExitThread() are known for not calling your class destructors resulting in memory leaks.

That is correct, but try to avoid such functions. Also from here :

There is no portable way in C++11 (that I'm aware of) to non-cooperatively kill a single thread in a multi-thread program (i.e. without killing all threads).

therefore just let the thread nicely finish, and destructors will be called.

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • That's right. As I wasn't getting any answers for this question I've asked a similar one on gamedev which got some more attention, but was then moved back to SO: http://stackoverflow.com/questions/22291060/what-is-the-reasoning-behind-two-step-object-destruction The answers I've got agree with what you're saying, plus solution such as this maybe useful when custom memory allocation is used. – jaho Mar 12 '14 at 22:46