Sign in to follow this  

A question about C++ streams

This topic is 2850 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So one of my friends was arguing with me saying that when you open a file you should explicitly close it. I told him that explicitly closing the file is worthless in cases where you can let the destructor do that job. And in fact writing fileName.close() just clutters the code since its not needed. What do you guys think? I mean why do something twice?

Share this post


Link to post
Share on other sites
Both are true.

It is similar to the discussion on if you should pass a file name to the constructor or if you should call open().



The basic_filebuf destructor is required to call close(). All your file streams are derived from it, so they will automatically close. A few other operations will also close a file stream as a side effect. So that is correct, you can ignore it and let the destructor do its job.

However, there are very good reasons for using the close() function:

If close fails for some reason --- such as not being able to write the data --- then it can set the failbit and optionally throw an exception if configured for it. You can perform intelligent handling in the fail case. You cannot test this with the destructor.

If you have a pool of files that are opened and closed, then obviously relying on the constructor and destructor are not appropriate. You will need to reuse the objects, and you will also want to reclaim the resources used by the file.




As for your argument about which you SHOULD do, that is an excellent policy debate.

Our coding practices are to explicitly open and close, check for errors, and handle errors as appropriate. Your coding practice may vary.

Share this post


Link to post
Share on other sites
>>However, there are very good reasons for using the close() function:

>>If close fails for some reason --- such as not being able to write the data --- then it can set the failbit and optionally throw an exception if configured for it. You can perform intelligent handling in the fail case. You cannot test this with the destructor.

Is this a some-what common occurrence? I am not sure.

>>If you have a pool of files that are opened and closed, then obviously relying on the constructor and destructor are not appropriate. You will need to reuse the objects, and you will also want to reclaim the resources used by the file.

I don't see how this serves as an argument since, I was saying that let
the destructor close the file when you can. And obviously you can't in that example.

Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
Did your friend say why they thought you should close the file explicitly?


He just said, if you open a file manually, then close the file manually.
Then he said, if you allocate memory then you wouldn't let the program terminate
to de-allocate it for you.

I asked him for a better argument, but he hasn't responded yet.

Share this post


Link to post
Share on other sites
Quote:
Then he said, if you allocate memory then you wouldn't let the program terminate
to de-allocate it for you.
Maybe frob has a point about error checking (to be honest, I've never given much thought to any failure cases that might arise when closing a file). That said, it sounds to me like your friend may just not fully understand RAII and/or how the SC++L file stream classes are implemented.

In other words, if he's saying that this:
void my_class::read_file(...)
{
std::ifstream file(...);

// Read data from file...
}
Would be better written as:
void my_class::read_file(...)
{
std::ifstream file(...);

// Read data from file...

file.close();
}
Then I think it's pretty safe to say that he is incorrect.

Share this post


Link to post
Share on other sites
Well, yes; but you don't normally open a file manually, either. There's an aesthetic argument to be made for matching a .open() call to a .close() call, but you normally just use the constructor, which is automatically matched to the destructor for you.

Of course, there's also precedent for not matching "explicit allocation" to the corresponding deallocation, when an RAII wrapper is involved: boost::shared_ptr. It is idiomatic to write something like 'x = boost::shared_ptr<foo>(new foo());' (and indeed, there's typically no reason to use shared_ptr for things that aren't heap-allocated), in which case "matching" with 'delete x.get();' would be incorrect.

Share this post


Link to post
Share on other sites

This topic is 2850 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this