Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Closing file streams

0 views
Skip to first unread message

John

unread,
Jan 27, 2009, 11:13:45 AM1/27/09
to
Hi,

If I open a file by

std::fstream* m_fstream;
m_fstream = new std::fstream("filename",std::ios_base::in);

what is the proper/best way to close the file?

1. delete m_fstream;

2. fstream.close();

3. fstream.close();
delete m_fstream.

My feeling is that I should do either 1 or 3. If I just delete the
pointer, is the file closed automatically, or do I need to close it
explicitly? 2 seems like it would still leave the stream allocated so
should be avoided.

What happens if m_fstream goes out of scope without deleting and/or closing?

Thanks,
John

Victor Bazarov

unread,
Jan 27, 2009, 11:54:26 AM1/27/09
to
John wrote:
> If I open a file by
>
> std::fstream* m_fstream;
> m_fstream = new std::fstream("filename",std::ios_base::in);
>
> what is the proper/best way to close the file?
>
> 1. delete m_fstream;

This should be fine. If the stream is open at the time of calling the
destructor, the stream will be closed for you.

>
> 2. fstream.close();

You mean

m_fstream->close();

, don't you? You can do it if you still intend to use 'm_fstream' for
something, like opening another file with it (using the syntax
m_fstream->open(args) ). But if you don't *ever* delete the pointer you
got from 'new', you'll have a memory leak.

>
> 3. fstream.close();
> delete m_fstream.

Again, this is just wrong and won't compile. You probably mean

m_fstream->close();
delete m_fstream;

which isn't necessary if you intend to dispose of 'm_fstream' anyway.
Closing is part of destruction of a stream object.

> My feeling is that I should do either 1 or 3. If I just delete the
> pointer, is the file closed automatically, or do I need to close it
> explicitly?

It's going to be closed for you.

> 2 seems like it would still leave the stream allocated so
> should be avoided.
>
> What happens if m_fstream goes out of scope without deleting and/or
> closing?

File not closed, memory leak. IOW, not good.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask

John

unread,
Jan 27, 2009, 12:06:02 PM1/27/09
to
Victor Bazarov wrote:
> John wrote:
>> If I open a file by
>>
>> std::fstream* m_fstream;
>> m_fstream = new std::fstream("filename",std::ios_base::in);
>>
>> what is the proper/best way to close the file?
>>
>> 1. delete m_fstream;
>
> This should be fine. If the stream is open at the time of calling the
> destructor, the stream will be closed for you.
>
>>
>> 2. fstream.close();
>
> You mean
>
> m_fstream->close();

Yes, I meant -> not .

Thanks for your help.

John

Pete Becker

unread,
Jan 27, 2009, 12:44:31 PM1/27/09
to

Or maybe you really meant . but created the wrong type <g>. The usual idiom is

std::ifstream str("filename");

When str goes out of scope, its destructor will close the file. C++ isn't Java.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Juha Nieminen

unread,
Jan 27, 2009, 12:53:21 PM1/27/09
to
John wrote:
> std::fstream* m_fstream;
> m_fstream = new std::fstream("filename",std::ios_base::in);

In general, you should avoid allocating objects dynamically like that,
unless it's *really* the only way to go. C++ is not Java.

I can't think of many situations where you would really need to
allocate a std::fstream object dynamically. The two most common
situations where you need a file stream are:

1) Inside a function which opens the file, reads/writes data, then
closes it before exiting. In this case there's absolutely no reason to
allocate the fstream object with 'new'. Allocating it dynamically is
only asking for trouble.
(Sometimes you will need to pass such a file stream as a reference
parameter to another function, but that makes no difference because
managing the allocation/deallocation is not the duty of that another
function.)

2) As a member variable of a class (so that the stream can outlive any
of the individual functions using it). However, in this case it should
usually also be a direct member instance, rather than the member being a
pointer to a dynamically allocated object. There's no benefit in
allocating it dynamically.
One could hastily argue that there can be a benefit in allocating it
dynamically if objects of this class are copied or assigned (in which
case all the copies will share the fstream object). However, you can't
do this safely without either deep-copying the stream (which would
happen anyways if you have it as a direct member variable, and without
having to explicitly write a copy constructor) or using reference
counting, which would be awkward for an fstream object which doesn't
have a reference counter. A much better solution in the latter case
would be to encapsulate the fstream into a reference-counting class, of
which the fstream is a member variable.

The only reason you theoretically might want to allocate a fstream
dynamically is if you want to share it. However, you can't share it
directly in C++ because C++ doesn't have GC. If only one object owns the
fstream object (and the sharing objects will release it before the
owning object) you could pass pointers or references around, but you
don't need to allocate the fstream object dynamically.

Ok, you could use a smart pointer to handle the shared, dynamically
allocated fstream object, which might be a plausible scenario, even if a
bit contrived.

> what is the proper/best way to close the file?
>
> 1. delete m_fstream;
>
> 2. fstream.close();
>
> 3. fstream.close();
> delete m_fstream.

The destructor of std::fstream closes the stream, so it makes little
difference whether you close it explicitly before destroying it or not.

You should close the stream explicitly when you want to make sure that
the stream is flushed and the resource freed at that exact point,
especially if the lifetime of the fstream object itself is much longer.

> What happens if m_fstream goes out of scope without deleting and/or
> closing?

Then you find out exactly why you shouldn't allocate fstream objects
dynamically: The stream gets never closed (well, not until the entire
program ends) and you have a memory leak.

John

unread,
Jan 27, 2009, 2:52:53 PM1/27/09
to

>> Yes, I meant -> not .
>>
>
> Or maybe you really meant . but created the wrong type <g>. The usual
> idiom is
>
> std::ifstream str("filename");
>
> When str goes out of scope, its destructor will close the file. C++
> isn't Java.
>

Well, I know nothing about java. The code I inherited has a pointer, so
it does use -> . It's too late to change it now.

John

unread,
Jan 27, 2009, 2:58:18 PM1/27/09
to

hi Juha,

Thanks for the detailed explanation. I see the points you make, but the
code I am working on is one that I didn't not originate and I am not
really responsible for that part of the code base. So, it's a little
too late to change away from the pointer. The pointer is wrapped in a
class that can handle many types of streams, so there is a reason a
pointer was used (not sure whether it is a sufficient reason).

John

Pete Becker

unread,
Jan 27, 2009, 6:28:16 PM1/27/09
to

Fair enough. <g>

James Kanze

unread,
Jan 28, 2009, 6:05:02 AM1/28/09
to
On Jan 27, 6:53 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> John wrote:
> > std::fstream* m_fstream;
> > m_fstream = new std::fstream("filename",std::ios_base::in);

> In general, you should avoid allocating objects dynamically
> like that, unless it's *really* the only way to go. C++ is not
> Java.

I agree with the conclusion: it's rare that I have a case where
a stream is dyanmically allocated. However, if we come back to
the three classical justifications for dynamic allocation:

-- A stream has identity (and is not copiable), so *if* the
lifetime doesn't correspond to a standard lifetime, you have
to use dynamic allocation . (I find it very rare for the
lifetime of a stream not to correspond to a standard
lifetime. But it's certainly occurs, e.g. log files.)

-- The size of a fstream is known, so unknown size doesn't
apply.

-- There are cases where the type is unknown, where you're
doing something like:

std::ostream* out = ( filename == "-"
? std::cout
: new std::ofstream( filename ) ) ;

(It's actually a bit more complicated than that; the above
is just to give an idea). Here again, dynamic allocation is
(or at least would seem) justified.

Typically, most of the time, I'd write something like:

if ( filename == "-" ) {
process( std::cin ) ;
} else {
std::ifstream in( filename ) ;
if ( ! in ) {
// error, could not open...
} else {
process( in ) ;
}
}

in this case, however, separating the open and close (or
destruction) and the processing into separate functions.

> I can't think of many situations where you would really need
> to allocate a std::fstream object dynamically. The two most
> common situations where you need a file stream are:

[...]


> 2) As a member variable of a class (so that the stream can
> outlive any of the individual functions using it). However, in
> this case it should usually also be a direct member instance,
> rather than the member being a pointer to a dynamically
> allocated object. There's no benefit in allocating it
> dynamically.
> One could hastily argue that there can be a benefit in allocating it
> dynamically if objects of this class are copied or assigned (in which
> case all the copies will share the fstream object). However, you can't
> do this safely without either deep-copying the stream (which would
> happen anyways if you have it as a direct member variable, and without
> having to explicitly write a copy constructor)

Stream objects have identity, and can't be copied. Any user
defined type which contains a stream object also has identity,
and probably shouldn't be copiable. (That's been my experience,
anyway.)

> or using reference counting, which would be awkward for an
> fstream object which doesn't have a reference counter.

boost::shared_ptr can easily be used with a stream object. If
reference counting is appropriate, it's the way to go.
(Reference counting isn't appropriate in as many cases as people
seem to think, but I can imagine scenarios with streams where it
definitely would be.)

> A much better solution in the latter case would be to
> encapsulate the fstream into a reference-counting class, of
> which the fstream is a member variable.

Why is this better than boost::shared_ptr?

> The only reason you theoretically might want to allocate a
> fstream dynamically is if you want to share it. However, you
> can't share it directly in C++ because C++ doesn't have GC.

??? There's something I'm not following here. What is the
relationship between sharing and GC. I'm very much in favor of
GC in general, but if there's one thing it doesn't apply to,
it's streams. Streams have an explicit end of lifetime, which
can even return an error state which must be handled. So their
lifetime must be explicitly managed.

And I'm not too sure what you mean by "sharing" here. A stream
allocated as a local variable can be passed by reference to any
number of other functions, and thus "shared". But since you
obviously know this, you must be referring to something else.

[...]


> > What happens if m_fstream goes out of scope without deleting
> > and/or closing?

> Then you find out exactly why you shouldn't allocate fstream
> objects dynamically: The stream gets never closed (well, not
> until the entire program ends) and you have a memory leak.

Not just a memory leak. Until the stream is closed, it uses
system resources as well. And if it is open in output, the data
may not even have been written. When the program ends, the file
descripter will also be recovered. If the stream was for
output, however, it's not certain that all of the data you wrote
will be physically output; the following program (on my system,
at least) results in an empty file:

int
main()
{
std::ofstream* f = new std::ofstream( "test.txt" ) ;
*f << "Hello, mec\n" ;
return 0 ;
}

All in all, streams are very special objects. If the
abstraction of a fstream is an open channel to a file, then they
have a zombie state (a state in which they are logically "dead",
although the object is still "alive" at the C++ language
level---I think Dave Abraham coined the term). And they have a
very explicit lifetime, with observable behavior at its end
(which means that they are not really candidates for garbage
collection). Both issues have to be dealt with.

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Juha Nieminen

unread,
Jan 28, 2009, 12:39:30 PM1/28/09
to
James Kanze wrote:
> And I'm not too sure what you mean by "sharing" here.

I was talking about shared ownership (rather than shared usage). In
other words, the last owner cleans up (and the last owner might change
from execution to execution). This obviously cannot be done with raw
pointers only.

James Kanze

unread,
Jan 29, 2009, 3:41:25 AM1/29/09
to

Interestingly, I've never encountered a case where shared
ownership (as a design concept) applied in a real application.
Sometimes, with C++, you use it in cases where there is no real
ownership at the design level, because the lack of garbage
collection in C++ means that you often need to artificially
introduce ownership into the design where it isn't appropriate,
but this is "implementation design". I don't think of it as
"shared ownership", but as a technical work-around of a weakness
in the language.

0 new messages