Recycle template in C ++ vs Java and C #

I have some experience in Java (and recently in C #) and would like to become better acquainted with C ++. I think I know some of the basics of memory (and other resource) management differences between these languages. Perhaps this is a small question related to the use of the dispose pattern and the various functions available in those languages ​​to help with it. I love what I've put together in the RAII and SBRM principles and am 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 pretty close analogue of C #

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.
}

      

Would one think that the idiom that best represents this behavior 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, in particular, to voice disputes about which language is better, and the like, but I wonder to what extent these implementations can be compared and how reliable they are in cases where the block uses the resource is faced with exceptional circumstances. I may have completely missed the point on something, and I was never really sure about the best methods for deleting - for the sake of argument, it might be worth assuming that all destroy / destroy functions here are idempotent - and really good advice on these matters might also have attitude to this issue.

Thanks for any pointers.

+3


source to share


5 answers


This is almost a sample. You don't actually need to add the function cleanup()

: the destructor should do the cleanup.

By the way, publishing open is cleanup()

allowed for random call cleanup()

, leaving the source code in an undesirable state.



class Resource {
    ~Resource() {
        //deal with any non-memory resources
    }
};   // allways ; at the end of a class ;-)

      

+3


source


This class (1) ,

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

      

is non-idiomatic and dangerous because (1) it provides an operation cleanup

and (2) prevents classes from being extracted from that and prevents automatic variables of that class.

Exposed cleanup

can be called at any time by any code, and after cleaning, you have an unusable zombie object . And you don't know when and if it will happen, and therefore the implementation code must check for this state. Very bad. It is on par with init

constructor functions , with only a stub constructor.

In practice, classes cannot be implemented, because in a derived class whose objects are destroyed, a call to the destructor of this class occurs, and this destructor is not available and ndash; so the code won't compile.

The correct template looks like this:



class Resource
{
public:
    // Whatever, then:

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

      

The destructor can still be named explicitly, but there is a strong incentive not to.

Note that with class derivation and polymorphic use, the destructor is better done virtual

. But in other cases, which would unnecessarily make the class polymorphic and therefore have a dimensional cost. So this is an engineering decision.


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

+2


source


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

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

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

      

And you control your lifetime with the usual block rules:

  void f() {
    C c;

    // stuff

    // before this exits, c will be destructed
  }

      

In fact, languages ​​like C # and Java try to imitate their templates. Since they don't have deterministic finalizers, you need to manually release unmanaged resources (using using

and try

accordingly). However, C ++ is completely deterministic, so it's much easier to do this.

0


source


Thanks for any pointers. Ha!

On the one hand, you are referring to the Java try method with the resource method, which is a shortcut to the actual call resource.close()

. Another alternative would callresource.Dispose()

It is important to remember that the object you use in Java and C # to close objects using these interfaces requires the object and element to be closed. The file should be closed after opening. There is no way to get around this, and try to get your way out of it, will leave you high and dry for memory, and leave other applications at risk for not having access to the files you claimed to be but never closed. It is important that you provide the code to close the files.

But there are other things to get rid of when objects leave memory. When these objects leave the scope, this happens. And that when Destructor is in C ++ you are calling what you stated above.

Closeable and IDisposable are what I call Responsible classes . They go above and beyond what any normal "destructor" for a class will be when it removes objects from scope and frees the top-level memory of the pointers you have. They will also take care that you may not think about it, or perhaps potentially put the system at risk later. He loves being a father and being a good father. A father should give his children refuge, but a good father knows what is best for the child, even when the children or other caregivers do not know what is best for them.

Note that it is important that you do AutoCloseable

not need to reference interfaces Closeable

if you want to use the "try with resources" alternative in Java.

Answer. The interface IDisposable

, Closeable

and even the interface, AutoCloseable

supports support for remote managed resources. Also the C ++ "Destructor", the grandfather of shorthand for such a deletion process. The problem is that you still need to ensure that the members of your destructive class are handled correctly. I think you have the correct function to call in C ++ to do what you want to do.

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

0


source


To summarize some of the good questions raised in other answers to this question and some other things I've read:

  • The answer to the main question: yes, an automatic local variable resource

    has its own destructor, which is called regardless of how the control leaves the block in which it is defined. In this respect, internal scoping and local allocation (usually implies stacks, not heaps, but is compiler dependent) of a variable (rather than using new

    ) is very similar to a try-with-resources block in Java or a block-block in C. #.
  • Unlike Java and C #, the ability to allocate objects purely local (usually means: on the stack) in C ++ means that for objects dealing with resources that need to be used safely, additional interface implementations and slightly overexposed methods public deletions are not required (and generally undesirable).
    • The use of the destructor private

      , ~Resource()

      eliminates some risk of accidental presence of objects in unexpected states (for example, file a writer without a file descriptor), but the "unmanaged resources" is still always safe located at the object is deleted (or goes out of scope, if it is an automatic local variable as in the example question.)
    • Using the public

      elements of the cleanup function is still perfectly possible if needed, but this is often an unnecessary hazard. If the cleanup contributor is to be made public, the destructor itself is best, as this is an obvious "self-documenting" directive to any user that it should only be called on very rare occasions: it's better to just use delete

      or let the locally allocated object fall out of scope and let the compiler make the call calling the destructor. It also removes any confusion that a public method without a destructor can cause ("should I call this object cleanup()

      before me delete

      or not?").
    • If a resource object needs to be inherited, it is important to make sure that its destructor is both virtual

      (overridden) and (at least as visible as) protected

      to ensure that subclasses can be disposed of properly.
  • In addition, with cleanup, implemented directly in destructors, and the semantics of destruction without garbage and a collector immediately after leaving the scope for automatic variables (and after delete

    for dynamically allocated variables ), it becomes the property and responsibility of the type itself, that it is necessarily properly disposed of, and not it can just be safely removed.

An example of a more idiomatic use of C ++:

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 the safe use of resources without leaks.

0


source







All Articles