lifetime of objects (here: strings)

Discussion in 'C++' started by Stefan Ram, Apr 10, 2009.

  1. Stefan Ram

    Stefan Ram Guest

    I still struggle with C++ memory management, so here are some
    questions:

    There seem to be three fundamental lifetimes of objects in C++?

    - scope-based lifetime (auto variables)
    - dynamic lifetime (»new«)
    - temporaries (expression based?)

    When »a« and »b« are instances of »::std::string«, what is the
    lifetime of »a+b«?

    A tutorial page, chosen at random,

    http://www.cprogramming.com/tutorial/string.html

    does not seem to mention or answer this question. Shouldn't
    this be very important for every programmer to know from the
    moment he starts to use objects?

    When a function returns a instance of a class, is it the most
    common case that it returns a temporary, so that this can be
    assumed, unless the documentation tells something else?

    Is it ok to return a temporary, as in the following example?

    ::std::string f()
    { ::std::string const a( "alpha" );
    ::std::string const b( "beta" );
    return a + b; }

    If this is a temporary, IIRC, it is guaranteed to exist during
    the evaluation of the full-expression of the call site? So one
    either has to pass it to another function there or copy it to
    an object with longer life time.

    The tutorial also uses:

    string my_string3 = my_string1 + my_string2;

    This seems to be »copy initialization«.

    Is it of any advantage to use »direct initialization« in this
    case? Which would be:

    string my_string3( my_string1 + my_string2 );

    Are the any guidelines, when to prefer copy initialization
    and when to prefer direct initialization?

    Ok, and I assume, what one can never to with a temporary
    object, is to bind a reference name to it, as in:

    string & my_string3( my_string1 + my_string2 );

    But, this would be allowed with a const reference, because
    for this special case ISO C++ extends the lifetime?

    string const & my_string3( my_string1 + my_string2 );

    Is this what »GotW #88« is about?

    So for the same reason, a function with a reference parameter
    can not be called with a temporary, unless the reference
    parameter is const?
     
    Stefan Ram, Apr 10, 2009
    #1
    1. Advertising

  2. Stefan Ram wrote:
    > I still struggle with C++ memory management, so here are some
    > questions:
    >
    > There seem to be three fundamental lifetimes of objects in C++?
    >
    > - scope-based lifetime (auto variables)
    > - dynamic lifetime (»new«)
    > - temporaries (expression based?)


    Actually if you count temporaries separate from 'scope-based', then
    there is the fourth - static.

    > When »a« and »b« are instances of »::std::string«, what is the
    > lifetime of »a+b«?


    It's a temporary.

    >
    > A tutorial page, chosen at random,
    >
    > http://www.cprogramming.com/tutorial/string.html
    >
    > does not seem to mention or answer this question. Shouldn't
    > this be very important for every programmer to know from the
    > moment he starts to use objects?
    >
    > When a function returns a instance of a class, is it the most
    > common case that it returns a temporary, so that this can be
    > assumed, unless the documentation tells something else?
    >
    > Is it ok to return a temporary, as in the following example?
    >
    > ::std::string f()
    > { ::std::string const a( "alpha" );
    > ::std::string const b( "beta" );
    > return a + b; }


    Yes, it is.

    > If this is a temporary, IIRC, it is guaranteed to exist during
    > the evaluation of the full-expression of the call site? So one
    > either has to pass it to another function there or copy it to
    > an object with longer life time.


    Yes.

    >
    > The tutorial also uses:
    >
    > string my_string3 = my_string1 + my_string2;
    >
    > This seems to be »copy initialization«.


    It is.

    > Is it of any advantage to use »direct initialization« in this
    > case? Which would be:
    >
    > string my_string3( my_string1 + my_string2 );


    No, in this case they are equivalent.

    > Are the any guidelines, when to prefer copy initialization
    > and when to prefer direct initialization?


    Always prefer direct initialisation.

    >
    > Ok, and I assume, what one can never to with a temporary
    > object, is to bind a reference name to it, as in:
    >
    > string & my_string3( my_string1 + my_string2 );
    >
    > But, this would be allowed with a const reference, because
    > for this special case ISO C++ extends the lifetime?
    >
    > string const & my_string3( my_string1 + my_string2 );


    Correct.

    > Is this what »GotW #88« is about?


    Probably. I didn't look.

    > So for the same reason, a function with a reference parameter
    > can not be called with a temporary, unless the reference
    > parameter is const?


    Yes.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Apr 10, 2009
    #2
    1. Advertising

  3. * Victor Bazarov:
    >
    > Always prefer direct initialisation.


    Depends on your criteria.

    I'd say "always prefer copy initialization".

    For readability and clarity, plus it's a bit more general and for newbies avoids
    the attempted-variable-declaration-that-turned-out-to-be-a-routine-declaration.


    Cheers,

    - Alf

    --
    Due to hosting requirements I need visits to <url: http://alfps.izfree.com/>.
    No ads, and there is some C++ stuff! :) Just going there is good. Linking
    to it is even better! Thanks in advance!
     
    Alf P. Steinbach, Apr 10, 2009
    #3
  4. Stefan Ram

    Stefan Ram Guest

    Victor Bazarov <> writes:
    >>But, this would be allowed with a const reference, because
    >>for this special case ISO C++ extends the lifetime?
    >>string const & my_string3( my_string1 + my_string2 );

    >Correct.


    Thank you!

    In the following block, all lines seems to be correct,
    but should line 3 should be preferred to line 2,
    because it avoids a copy operation and therefore is faster?

    #include <string>

    ....

    { ::std::string const a( "alpha" ); /* line 0 */
    ::std::string const b( "beta" ); /* line 1 */
    ::std::string c( a + b ); /* line 2 */
    ::std::string const & d( a + b ); /* line 3 */

    /* possible continuation of the block,
    possibly using a, b, c, and d. */ }
     
    Stefan Ram, Apr 10, 2009
    #4
  5. Stefan Ram wrote:
    > Victor Bazarov <> writes:
    >>> But, this would be allowed with a const reference, because
    >>> for this special case ISO C++ extends the lifetime?
    >>> string const & my_string3( my_string1 + my_string2 );

    >> Correct.

    >
    > Thank you!
    >
    > In the following block, all lines seems to be correct,
    > but should line 3 should be preferred to line 2,
    > because it avoids a copy operation and therefore is faster?
    >
    > #include <string>
    >
    > ...
    >
    > { ::std::string const a( "alpha" ); /* line 0 */
    > ::std::string const b( "beta" ); /* line 1 */
    > ::std::string c( a + b ); /* line 2 */
    > ::std::string const & d( a + b ); /* line 3 */
    >
    > /* possible continuation of the block,
    > possibly using a, b, c, and d. */ }
    >


    It's OK to use a ref to const if you don't care about re-using the
    object or changing it. Keep in mind though, that it still makes the
    code somewhat convoluted, and that is justified by what seems to be
    premature optimisation. It's easier on a human brain to deal with 'c'
    since it's an object (and there is an object behind 'd' as well), and
    the copying of this kind is usually taken care of by the optimizing
    compiler.

    Optimize what you've measured.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Apr 10, 2009
    #5
  6. Stefan Ram

    James Kanze Guest

    On Apr 10, 7:36 pm, -berlin.de (Stefan Ram) wrote:
    > I still struggle with C++ memory management, so here are some
    > questions:


    > There seem to be three fundamental lifetimes of objects in C++?


    > - scope-based lifetime (auto variables)
    > - dynamic lifetime (»new«)
    > - temporaries (expression based?)


    There are more than that. There's static lifetime, of
    course, and there's the lifetime of objects thrown as
    exceptions, and there may be a few others that I've
    forgotten. (IIRC, function arguments also have a special
    lifetime.)

    > When »a« and »b« are instances of »::std::string«, what is the
    > lifetime of »a+b«?


    It's a temporary, so the lifetime is until the end of the
    full expression. With a couple of obvious exceptions, and
    one not so obvious one, but at least until the end of the
    full expression.

    > A tutorial page, chosen at random,


    Don't choose your tutorials at random. There's a lot of
    junk out there. (No comment on the link you give---I've not
    seen it. But if you reall choose at random, you're more
    likely to get junk than anything worthwhile.)

    > http://www.cprogramming.com/tutorial/string.html


    > does not seem to mention or answer this question. Shouldn't
    > this be very important for every programmer to know from the
    > moment he starts to use objects?


    It depends on the object. For objects like std::string, all
    you really need to know is the minimum lifetime, and most of
    the time, not even that.

    > When a function returns a instance of a class, is it the
    > most common case that it returns a temporary, so that
    > this can be assumed, unless the documentation tells
    > something else?


    By definition, the return value of a function is a
    temporary.

    > Is it ok to return a temporary, as in the following example?


    > ::std::string f()
    > { ::std::string const a( "alpha" );
    > ::std::string const b( "beta" );
    > return a + b; }


    Yes.

    > If this is a temporary, IIRC, it is guaranteed to exist
    > during the evaluation of the full-expression of the call
    > site? So one either has to pass it to another function
    > there or copy it to an object with longer life time.


    There's (officially) a copy. Officially, because the
    standard explicitly gives the compiler the right to
    eliminate it, by merging the temporary a+b with the
    temporary returned from the function. Most compilers
    actually do this optimization.

    > The tutorial also uses:


    > string my_string3 = my_string1 + my_string2;


    > This seems to be »copy initialization«.


    Formally.

    > Is it of any advantage to use »direct initialization« in this
    > case? Which would be:


    > string my_string3( my_string1 + my_string2 );


    Since the type of string1+string2 is the same as the target
    type, the two are effectively the same.

    > Are the any guidelines, when to prefer copy
    > initialization and when to prefer direct initialization?


    Do what the house coding guidelines say.

    > Ok, and I assume, what one can never to with a temporary
    > object, is to bind a reference name to it, as in:


    > string & my_string3( my_string1 + my_string2 );


    > But, this would be allowed with a const reference,
    > because for this special case ISO C++ extends the
    > lifetime?


    Exact. But the effect is not transitive; the lifetime is
    only extended to match the reference initialized with the
    temporary, not to other references which were initialized
    from that reference.

    > string const & my_string3( my_string1 + my_string2 );


    > Is this what »GotW #88« is about?


    Yes.

    > So for the same reason, a function with a reference
    > parameter can not be called with a temporary, unless the
    > reference parameter is const?


    Yes, but that has nothing to do with lifetime. It's just a
    rule. The original rule allowed binding a temporary to any
    reference. People got into trouble with temporaries that
    were the result of implicit conversions, e.g.:

    void
    incr( int& i )
    {
    ++ i ;
    }

    void
    f()
    {
    unsigned j = 42 ;
    incr( j ) ; // does *NOT* change j.
    }

    So the rule was changed.

    --
    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, Apr 10, 2009
    #6
  7. Stefan Ram

    James Kanze Guest

    On Apr 10, 7:53 pm, "Alf P. Steinbach" <> wrote:
    > * Victor Bazarov:


    > > Always prefer direct initialisation.


    > Depends on your criteria.


    > I'd say "always prefer copy initialization".


    > For readability and clarity, plus it's a bit more general
    > and for newbies avoids the
    > attempted-variable-declaration-that-turned-out-to-be-a-routine-declaration.


    The rule is simple: alway prefer copy initialization, to
    avoid the most embarrassing parse you refer to, and always
    prefer direct initialization, so that novice readers won't
    confuse it with assignment.

    You're damned if you do, and damned if you don't.

    (Also, I can remember one very old compiler which didn't
    accept direct initialization for basic types, pointers and
    references.)

    --
    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, Apr 10, 2009
    #7
  8. Stefan Ram

    James Kanze Guest

    On Apr 10, 8:07 pm, -berlin.de (Stefan Ram) wrote:
    > In the following block, all lines seems to be correct,
    > but should line 3 should be preferred to line 2,
    > because it avoids a copy operation and therefore is faster?


    > #include <string>


    > ...


    > { ::std::string const a( "alpha" ); /* line 0 */
    > ::std::string const b( "beta" ); /* line 1 */
    > ::std::string c( a + b ); /* line 2 */
    > ::std::string const & d( a + b ); /* line 3 */
    >
    > /* possible continuation of the block,
    > possibly using a, b, c, and d. */ }


    No. Line 3 should be avoided, because it isn't idiomatic,
    and because it depends on a less known and less obvious
    feature. In general, std::string is designed to have value
    semantics, so you use it like a value, i.e. like an int.
    And don't worry about the rest. (The only time the rest
    would be relevant is when you have to expose the internals,
    e.g. using c_str() because you have to pass the pointer to a
    legacy function.)

    --
    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, Apr 10, 2009
    #8
  9. Stefan Ram

    Stefan Ram Guest

    James Kanze <> writes:
    >feature. In general, std::string is designed to have value
    >semantics, so you use it like a value, i.e. like an int.
    >And don't worry about the rest. (The only time the rest
    >would be relevant is when you have to expose the internals,
    >e.g. using c_str() because you have to pass the pointer to a
    >legacy function.)


    Thanks!

    So, unless »c_str()« (or »new« or »delete«) is used, there is
    no means to obtain a reference to a ::std::string object, that
    already has ceased to exist?

    Could a malicious programmer somehow construct code that is
    accepted by the compiler, does not use »new« or »delete«, and
    still has either a memory leak or a use of or a reference to
    an object that already has ceased to exist?

    ~~

    Maybe using the property that the lifetime extension for
    temporaries is not transitive?

    ::std::string const & c( a + b );

    What do I have to do now with »c« to intentionally create a
    bug where this lack of transitivity matters?
     
    Stefan Ram, Apr 10, 2009
    #9
  10. Stefan Ram schrieb:
    > Could a malicious programmer somehow construct code that is
    > accepted by the compiler, does not use »new« or »delete«, and
    > still has either a memory leak or a use of or a reference to
    > an object that already has ceased to exist?


    int& badFunction()
    {
    int i;
    return i;
    }

    This sometimes happens with local arrays used for c-style strings.
    The variable is gone, but the function returns a reference or pointer
    referring to it.

    > ~~
    >
    > Maybe using the property that the lifetime extension for
    > temporaries is not transitive?
    >
    > ::std::string const & c( a + b );
    >
    > What do I have to do now with »c« to intentionally create a
    > bug where this lack of transitivity matters?
    >


    std::string const& bad =
    std::max( std::string("bad"), std::string("code") );

    Lifetime is extended only by direct initialization of a reference by a
    temporary.

    So the lifetime of a reference, that is bound only indirectly to a
    temporary through other references, is not extended. Do you mean that by
    transitivity?

    --
    Thomas
     
    Thomas J. Gritzan, Apr 10, 2009
    #10
  11. Stefan Ram

    Stefan Ram Guest

    "Thomas J. Gritzan" <> writes:
    >Do you mean that by transitivity?


    Thank you!

    When I used that word, I referred to James Kanze's wording:

    »But the effect is not transitive; the lifetime is only
    extended to match the reference initialized with the
    temporary, not to other references which were initialized
    from that reference.«
     
    Stefan Ram, Apr 10, 2009
    #11
  12. Stefan Ram

    Stefan Ram Guest

    James Kanze <> writes:
    >Exact. But the effect is not transitive; the lifetime is
    >only extended to match the reference initialized with the
    >temporary, not to other references which were initialized
    >from that reference.
    >>string const & my_string3( my_string1 + my_string2 );


    I am trying to construct an example where this intransitivity
    can be seen. First attempt:

    string const & a( "a" ); /* line 0 */
    string const & b( "b" ); /* line 1 */
    string const & c( a + b ); /* line 2 */
    string const & d( c ); /* line 3 */
    ....

    Line 0 and line 1 bind »a« and »b« to temporaries.
    In line 2, »a + b« creates another temporary object, called »c«.
    In line 3, the intransitivity seems to apply, but since the scope
    of »d« is a part of the scope of »c«, the object »d« will live as
    long as »c« lives, so »d« will also live until control exits
    the block. So far, there should be no »dangling references«.

    For the same reason, it should be okay to call a function
    »void f( ::std::string const & x );« with »c«: Because the
    lifetime of the incarnation of this function is a part of the
    lifetime of the block containing the reference »c«.

    One might try to create a dangling reference by »return d;«
    in »::std::string const & f(){ /* code from above */ return d; }«.
    This will indeed succeed (to create a dangling reference).
    So is this the situation, where the intransitivity can be observed?

    But the following also seems to return a dangling reference,
    while the result is the first const reference the temporary is
    bound to.

    ::std::string const & f(){ return "a"; }
     
    Stefan Ram, Apr 11, 2009
    #12
  13. Stefan Ram

    James Kanze Guest

    On Apr 11, 5:19 pm, -berlin.de (Stefan Ram) wrote:
    > James Kanze <> writes:
    > >Exact. But the effect is not transitive; the lifetime is
    > >only extended to match the reference initialized with the
    > >temporary, not to other references which were initialized
    > >from that reference.
    > >>string const & my_string3( my_string1 + my_string2 );


    > I am trying to construct an example where this
    > intransitivity can be seen.


    std::string const f( std::string const& arg )
    {
    return arg ;
    }

    void
    g()
    {
    std::string const& s( f( std::string( "temp" ) ) ) ;
    // ...
    }

    > One might try to create a dangling reference by »return d;«
    > in »::std::string const & f(){ /* code from above */ return
    > d; }«. This will indeed succeed (to create a dangling
    > reference). So is this the situation, where the
    > intransitivity can be observed?


    Two cases, really. (Maybe more, but two occur off hand.) As
    above, using the temporary to initialize a reference to a
    function which returns it, and using the reference in the
    initialization of a dynamically allocated object.

    > But the following also seems to return a dangling reference,
    > while the result is the first const reference the temporary
    > is bound to.


    > ::std::string const & f(){ return "a"; }


    Yes. But this is more or less the classical "don't return a
    reference or a pointer to a local variable or temporary. There
    are two absolute limits to the lifetime of a temporary: the full
    expression it is created in is the lower limit (unless one
    considers function parameters temporaries), and the scope in
    which the temporary is created is the upper one---all rules
    extending the lifetime take second rang to this one.

    A good example of this second rule coming into play:

    class C { C() ; std::string const& rs ; } ;

    C::C()
    : rs( "abc" )
    {
    }

    Clearly, rs is initialized with a temporary. Directly, so the
    lifetime should be extended. Which it is, but only until the
    end of the constructor. (The reason for this is simple: where
    does the compiler put the temporary if its lifetime is to
    outlive the call to the constructor?)

    Finally: I would like to one again stress that std::string is a
    value type. You should use it exactly like you would int or
    double, at least with regards to whether to use a reference or
    not. You don't generally have local variables or members which
    are references to int or double, and you shouldn't generally
    have local variables or members which are references to string.
    (There are exceptions, of course, but these generally involve
    references to objects which outlive the function.)

    --
    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, Apr 12, 2009
    #13
  14. * James Kanze:
    > On Apr 11, 5:19 pm, -berlin.de (Stefan Ram) wrote:
    >> James Kanze <> writes:
    >>> Exact. But the effect is not transitive; the lifetime is
    >>> only extended to match the reference initialized with the
    >>> temporary, not to other references which were initialized
    >> >from that reference.
    >>>> string const & my_string3( my_string1 + my_string2 );

    >
    >> I am trying to construct an example where this
    >> intransitivity can be seen.

    >
    > std::string const f( std::string const& arg )
    > {
    > return arg ;
    > }


    Typo, you meant

    std::string const& f( std::string const& arg )

    Yes?


    >
    > void
    > g()
    > {
    > std::string const& s( f( std::string( "temp" ) ) ) ;
    > // ...
    > }
    >


    Cheers,

    - Alf (typo-spotter)

    --
    Due to hosting requirements I need visits to <url: http://alfps.izfree.com/>.
    No ads, and there is some C++ stuff! :) Just going there is good. Linking
    to it is even better! Thanks in advance!
     
    Alf P. Steinbach, Apr 12, 2009
    #14
  15. Stefan Ram

    James Kanze Guest

    On Apr 12, 3:05 pm, "Alf P. Steinbach" <> wrote:
    > * James Kanze:
    > > On Apr 11, 5:19 pm, -berlin.de (Stefan Ram) wrote:
    > >> James Kanze <> writes:
    > >>> Exact. But the effect is not transitive; the lifetime is
    > >>> only extended to match the reference initialized with the
    > >>> temporary, not to other references which were initialized
    > >> >from that reference.
    > >>>> string const & my_string3( my_string1 + my_string2 );


    > >> I am trying to construct an example where this
    > >> intransitivity can be seen.


    > > std::string const f( std::string const& arg )
    > > {
    > > return arg ;
    > > }


    > Typo, you meant


    > std::string const& f( std::string const& arg )


    > Yes?


    Yes. Thanks for the correction.

    --
    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, Apr 13, 2009
    #15
    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. Simon Elliott

    lifetime of static objects

    Simon Elliott, Oct 11, 2004, in forum: C++
    Replies:
    7
    Views:
    499
    Simon Elliott
    Oct 12, 2004
  2. Simon Elliott

    Lifetime of static objects revisited

    Simon Elliott, Dec 2, 2004, in forum: C++
    Replies:
    5
    Views:
    421
    Simon Elliott
    Dec 4, 2004
  3. Ben

    Strings, Strings and Damned Strings

    Ben, Jun 22, 2006, in forum: C Programming
    Replies:
    14
    Views:
    788
    Malcolm
    Jun 24, 2006
  4. Elie Grouchko

    lifetime of ASP objects

    Elie Grouchko, May 22, 2005, in forum: ASP General
    Replies:
    0
    Views:
    116
    Elie Grouchko
    May 22, 2005
  5. George Hester

    Try over here likely more to the point here

    George Hester, Sep 30, 2004, in forum: Javascript
    Replies:
    0
    Views:
    120
    George Hester
    Sep 30, 2004
Loading...

Share This Page