Re: Closing file streams

Discussion in 'C++' started by Juha Nieminen, Jan 27, 2009.

  1. 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.
    Juha Nieminen, Jan 27, 2009
    #1
    1. Advertising

  2. Juha Nieminen

    James Kanze Guest

    On Jan 27, 6:53 pm, Juha Nieminen <> 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::eek:stream* out = ( filename == "-"
    ? std::cout
    : new std::eek: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::eek:fstream* f = new std::eek: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
    James Kanze, Jan 28, 2009
    #2
    1. Advertising

  3. 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.
    Juha Nieminen, Jan 28, 2009
    #3
  4. Juha Nieminen

    James Kanze Guest

    On Jan 28, 6:39 pm, Juha Nieminen <> wrote:
    > 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.


    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.

    --
    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
    James Kanze, Jan 29, 2009
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ZOCOR

    Closing IO Streams

    ZOCOR, Aug 27, 2004, in forum: Java
    Replies:
    3
    Views:
    512
    Michael Borgwardt
    Aug 27, 2004
  2. Knute Johnson

    Closing sockets and streams?

    Knute Johnson, Jan 21, 2006, in forum: Java
    Replies:
    14
    Views:
    648
    Thomas Hawtin
    Jan 24, 2006
  3. Evgeni Sergeev
    Replies:
    2
    Views:
    440
    Jean Brouwers
    Dec 28, 2004
  4. =?Utf-8?B?Vk1J?=

    Closing popup window when closing parent window?

    =?Utf-8?B?Vk1J?=, Feb 14, 2007, in forum: ASP .Net
    Replies:
    3
    Views:
    650
    Thomas Hansen
    Feb 15, 2007
  5. Philipp

    Closing decorated streams

    Philipp, Jul 9, 2009, in forum: Java
    Replies:
    6
    Views:
    321
    Knute Johnson
    Jul 11, 2009
Loading...

Share This Page