2

Suppose a class like this:

class A {
private:
   QFile file;

public:
   A::A(QFile file): file(file) {}

   void doSomething() {
      file.open(QIODevice::WriteOnly); 
      // ... do operations that can throw an exception
      file.close();
   } 
}

if something occurs, the close() never calls it. The correct will be use try - finally, but C++ doesn't support it:

class A {
private:
   QFile file;

public:
   A::A(QFile file): file(file) {}

   void doSomething() {
      file.open(QIODevice::WriteOnly); 
      try {
          // ... do operations that can throw an exception
      }
      finally {
          file.close();
      }
   } 
}

How can I do it on C++?

mabg
  • 1,894
  • 21
  • 28
  • 5
    [Finally? What `finally`? We ain't need no stinkin' `finally` :-)](http://stackoverflow.com/q/161177/335858) – Sergey Kalinichenko Apr 09 '14 at 14:03
  • @dasblinkenlight: classy ;-) – Bathsheba Apr 09 '14 at 14:03
  • @dasblinkenlight The top rated answer still manages to miss the most important distinction between C#'s `using` and RAII: C# still requires the client code to do something (declare a `using`); in C++, the client code would have to take active measures to avoid the correct clean-up (e.g. allocating the object dynamically without deleting it). – James Kanze Apr 09 '14 at 14:22
  • FYI, according to the documentation for `QFile`, the destructor closes it for you. – Tyler Jandreau Apr 09 '14 at 14:31
  • Yes, I know it, but it isn't the case. The case is that the QFile is passed on the constructor, so it doesn't destroyed until the class A destroys, but on the method is called open and must close at the end. I think that it is a case that RAII doesn't work and must to do with another technique. – mabg Apr 09 '14 at 15:06

2 Answers2

5

The usual solution is to use RAII: in this case, for example, if QFile has a "correct" destructor, just declaring it as a local variable should do the trick:

void A::doSomething()
{
    QFile file;
    file.open(...);
    //  ...
    file.close();   //  So you can check that everything when right.
}

The file should automatically be closed when QFile is destructed, although if it wasn't closed before, you won't be able to check the status (and the data in the file might be incomplete).

If for some reason this isn't viable, you might want to use a scoped wrapper class:

class QFileWrapper
{
    QFile* myFile;
public:
    QFileWrapper( QFile* file ) : myFile( file ) {}
    ~QFileWrapper() { myFile->close(); }
};

I actually do this a lot for std::ofstream, with a commit function, and it is the constructor which takes the filename, and does the open. The commit function closes the file, and if the close succeeds, sets a flag that the file has been committed. The destructor tests the flag, and if the file hasn't been committed, it closes it and deletes it, so that no partial file is left lying around.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Are you saying to do something like this on the doSomething()?: QFileWrapper fileWrapper(file); fileWrapper.open(); ... – mabg Apr 09 '14 at 15:08
  • I think that it works, because QFileWrapper is a local variable and RAII call the destructor at the end. Thanks. – mabg Apr 09 '14 at 15:10
0

The following code is broadly similar:

try {    
    /*do something that may throw an exception*/    
} catch (.../*the ellipsis means catch anything*/){   
    /* any cleanup */
    throw; /*this rethrows the exception, not compulsory to include*/
}

It's not quite the same since the catch block will only be entered if an exception is thrown (unlike a finally block in Java).

But C++ doesn't need a finally construct since C++ objects have destructors that can be used to perform any cleanup.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483