4

I have some background in Java (and recently in C#) and would like to get to know C++ better as well. I think I'm aware of some of the basics of the differences in memory (and other resource) management between these languages. This is perhaps a small question relating to using the dispose pattern and the different features available in these languages to assist with it. I like what I've gathered of the RAII and SBRM principles and I'm trying to understand them further.

Suppose I have the following class and method in Java

class Resource implements Closeable {
    public void close() throws IOException {
        //deal with any unmanaged resources
    }
}
...
void useSomeResources() {
    try(Resource resource = new Resource()) {
        //use the resource
    }
    //do other things. Resource should have been cleaned up.
}

or a fairly close C# analogue

class Resource : IDisposable
{
    public void Dispose()
    {
        //deal with any unmanaged resources
    }
}
...
void UseSomeResources()
{
    using(var resource = new Resource())
    {
        //use the resource
    }
    //do other things. Resource should have been cleaned up.
}

Am I right to think that the idiom best representing this same behaviour in C++ would be the following?

class Resource {
    ~Resource() {
        cleanup();
    }
    public:
    void cleanup() {
        //deal with any non-memory resources
    }
};
...
void useSomeResources()
{
    {
        Resource resource;
        //use the resource
    }
    //do other things. Stack allocated resource
    //should have been cleaned up by stack unwinding
    //on leaving the inner scope.
}

I don't want, especially, to elicit debate over whose language is better and things like that, but I'm wondering to what extent these implementations can be compared, and how robust they all are to cases where the block using the resource encounters exceptional circumstances. I may have completely missed the point on something, and I'm never quite sure about best practices for disposal - for the sake of argument, perhaps it's worth assuming all disposal/destruction functions here are idempotent - and really good tips for those matters might also be relevant to this question.

Thanks for any pointers.

Oly
  • 2,329
  • 1
  • 18
  • 23

5 Answers5

4

It's almost the pattern. In fact you don't need to add a cleanup() function: the destructor is there to do the cleanup.

By the way, having a public cleanup() exposed, allows for accidental call of cleanup(), bringing the ressource in an undesired state.

class Resource {
    ~Resource() {
        //deal with any non-memory resources
    }
};   // allways ; at the end of a class ;-)
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thanks for this - in fact I wondered about the access level on that method. I figured that to parallel the Dispose() and close() methods, which can also be called at any time, it would be public. I agree that it's a dangerous way to do it unless care is taken, which is part of the reason I really like what I understand of SBRM! Have added the semicolon. Rookie error. – Oly Aug 01 '15 at 19:50
  • 1
    P.s that's partly why I mentioned idempotency for the cleanup, but of course the problem of unexpectedly prematurely cleaned up objects still remains. I think I am going to like C++ :) – Oly Aug 01 '15 at 19:57
  • With the class as shown when I'm writing this, automatic variables can't be declared. That makes it less than ideal. – Cheers and hth. - Alf Aug 03 '15 at 09:26
2

This (1)proposed class,

class Resource {
    ~Resource() {
        cleanup();
    }
    public:
    void cleanup() {
        //deal with any non-memory resources
    }
};

is non-idiomatic and dangerous because (1) it exposes the cleanup operation, and (2) it prevents deriving classes from this, and prevents automatic variables of this class.

The exposed cleanup can be called at any time by any code, and after cleanup you have non-usable zombie object. And you do not know when or if that happens, and so the implementation code has to check for that state everywere. Very ungood. It's on a par with init functions taking the roles of constructors, with just a dummy constructor.

Classes can not in practice be derived because in a derived class whose objects are destroyed, a call to this class' destructor is generated, and that destructor is inaccessible – so the code won't compile.

The proper pattern looks like this:

class Resource
{
public:
    // Whatever, then:

    ~Resource()
    {
        // Clean up.
    }
};

The destructor can still be called explicitly, but there's a strong incentive not to do so.

Note that with class derivation and polymorphic use, the destructor should better be made virtual. But in other cases that would needlessly make the class polymorphic and thus have a size cost. So it's an engineering decision.


(1) I added a missing semicolon. It's a good idea to post real code, even for small general examples.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Thanks for pointing out the missing semicolon. Rookie error! As in my answer to @Christophe, I appreciate the tips on making it more idiomatic - seems like the existence of the IDisposable and Closeable interface are workarounds to deal with the fact that stack allocation of local objects isn't possible in these languages. So having the destruction method(s) private makes a lot of sense in C++. If a destructor is called explicitly, this doesn't in fact delete the object, though, am I right? So the difference is just that the code self-documents better, saying, 'call this at your own risk'? – Oly Aug 01 '15 at 20:02
  • 1
    @Oly'Oil'Sourbut: re "having the destruction method(s) private makes a lot of sense in C++", probably you're talking here about a `cleanup` method called from the destructor. But in case you're not, note that as mentioned having a `private` destructor prevents both automatic variables and (effectively, in practice) class derivation. A `protected` destructor prevents only automatic variables, so is one way to practically enforce only dynamic allocation (another way, that once was recommended, is to make constructors non-`public` and provide factory functions, which is much the Java and C# way). – Cheers and hth. - Alf Aug 01 '15 at 20:30
  • @Oly'Oil'Sourbut: re " If a destructor is called explicitly, this doesn't in fact delete the object, though, am I right?", technically you're right: it doesn't do a deallocation. But it screws up the class invariant, the assumptions that other methods rely on. Destructors are usually called only automatically, and then you're guaranteed one and exactly one destructor call per destroyed object. Just apropos, Visual C++ 6.0, in the late 1990s, had a bug where it would call the destructor of an exception object twice. That was a bit nasty, but happily it only happened when doing "advanced" stuff. – Cheers and hth. - Alf Aug 01 '15 at 20:33
0

You already mentioned the answer, it's RAII, just like in your link.

A typical class in C++ will have a (virtual! you forgot that) destructor:

  class C { 
    virtual ~C { /*cleanup*/ }
  };

And you control its lifetime with normal block rules:

  void f() {
    C c;

    // stuff

    // before this exits, c will be destructed
  }

This is in fact what languages like C# and Java try to emulate with their dispose patterns. Since they don't have deterministic finalizers you have to manually release unmanaged resources (using using and try respectively). However, C++ is completely deterministic, so it's a lot simpler to do this.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 4
    A virtual destructor is not necessary, and certainly not typical in my code. – Benjamin Lindley Aug 01 '15 at 18:58
  • 1
    There is no point in making the destructor virtual if inheritance isn't involved. – bku_drytt Aug 01 '15 at 19:01
  • Make sure to downvote well because you don't use them! – Blindy Aug 01 '15 at 19:46
  • I'm (somewhat) aware of how inheritance and virtual methods are handled in C++, but I wanted to pare the code examples I gave down to a minimum. Certainly worth remembering to make destructors virtual when inheritance is involved though; thanks! – Oly Aug 01 '15 at 19:54
0

Thanks for any pointers. Ha!

One thing you're referring to the Java try with resources method, which is a shortcut to actually calling resource.close(). The other alternative would call resource.Dispose()

The important thing to remember is that the object that you're using in Java and C# to close things using these interfaces require object and member-field closure. A file must be closed, once opened. There's no way around it, and trying to weasel your way out of that, will leave you high and dry for memory, and will leave other applications at risk for failing for not having access to the files you`ve laid claim to but have never closed. It's important that you provide code to make files be closed.

But there are other things that have to be gotten rid of when objects leave memory. When those objects leave scope, those things happen. And that's when the Destructor in C++, what you have referenced above gets called.

Closeable and IDisposable belong to what I call “Responsible” classes however. They go above and beyond what any normal “destructor” for a class would when it removes objects from scope and frees top level memory of pointers that you have available. They also take care of the rigamarole of things you may not think about or could potentially put the system at risk for later. It's like being a father versus being a good father. A father has to give his kids shelter, but a good father knows what's best for the kid even when the kids or other caretakers don`t know what's best for them.

Note that it's important to reference AutoCloseable interfaces not necessarily Closeable interfaces when you want to use Java`s “try with Resources” alternative.

The answer: The IDisposable, the Closeable interface, and even the AutoCloseable interface, all support the removal of managed resources. So does the “Destructor” in C++, the grandaddy of shorthand for such a removal process. The problem is that you still have to ensure that you handle properly the members of your class undergoing destruction. I think you have the right function in mind to call in C++ to do what you want to do.

References: http://www.completecsharptutorial.com/basic/using.php http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

JonAar Livernois
  • 344
  • 2
  • 12
0

To summarise some good points raised in other answers to this question and some other things I've read:

  • The answer to the main question is yes, the automatic local variable resource has its destructor called regardless of how control leaves the block in which is it defined. In this respect the inner scope and local allocation (usually implies stack rather than heap, but depends on compiler) of the variable (rather than using new) act very like a try-with-resources block in Java, or a using block in C#.
  • In contrast to Java and C#, the ability to allocate objects purely locally (normally meaning: to the stack) in C++ means that, for objects dealing with resources which need disposing of safely, additional interface implementations and somewhat overexposed public disposal methods are not needed (and usually not desirable).
    • Using a private destructor, ~Resource(), removes some of the danger of accidentally having objects in unexpected states (e.g file writer without file handle), but 'unmanaged resources' are still always safely disposed when the object is deleted (or goes out of scope if it is an automatic local variable as in the question example.)
    • Using public cleanup function members is still absolutely possible if this is desired, but it is often an unnecessary danger. If a cleaning up member must be made public, it's best to be the destructor itself, since this is an obvious 'self-documenting' indication to any user that it should only be called in very rare circumstances: better to just use delete or let a locally allocated object fall out of scope and let the compiler do the work of calling the destructor. It also removes any confusion a non-destructor public method might cause ('should I call cleanup() before I delete this object or not?').
    • If your resource object is to be inherited from, it is important to ensure its destructor is both virtual (overridable) and (at least as visible as) protected, to ensure that subclasses can be disposed of properly.
  • Further, with cleanup implemented directly in destructors, and the garbage-collector-less semantics of destruction immediately upon leaving scope for automatic variables (and upon delete for dynamically-allocated variables), it becomes a property and responsibility of the type itself that it is necessarily properly disposed of, rather than that it is simply capable of being disposed of safely.

An example of more idiomatic C++ usage:

class Resource {
    //whatever members are necessary
    public:
    //resource acquisition for which this object is responsible should be constrained to constructor(s)
    ~Resource() { //or virtual ~Resource() if inheritance desired
        //deal with resource cleanup
    }
};

When used as suggested in the question, this approach should ensure resources are dealt with safely without leaks occurring.

Oly
  • 2,329
  • 1
  • 18
  • 23