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:

stream* out = ( filename == "-"
? std::cout
: new std:

fstream( 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:

fstream* f = new std:

fstream( "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:
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