How Methods Return Objects

Discussion in 'C++' started by cppaddict, Jul 30, 2004.

  1. cppaddict

    cppaddict Guest

    I'd like to know what goes on under the hood when methods return
    objects. Eg, I have a simple Point class with two members _x and _y.
    It's constructor, copy constructor, assignment operator and additon
    operator (which returns another Point object, and which my question is
    about) are as follows:

    Point::point(int x, int y) :
    _x(x),
    _y(y) { }

    Point::point(const Point& p) {
    _x = p._x;
    _y = p._y;
    }

    Point& Point::eek:perator=(const Point& p) {
    _x = p._x;
    _y = p._y;
    return (*this);
    }

    Point Point::eek:perator+(const Point& p) const {
    Point ret(_x + p._x, _y + p._y);
    return ret;
    }

    What happens when operator+ returns the temporary Point object that it
    creates?

    Does it use the copy constructor to create a copy of the temporary
    variable "ret"? Is ret destroyed after that?

    If not, how does it return a Point object?

    Also, why is that "new" never comes into play? How can the compiler
    know beforehand how many Point objects will be created via operator+?
    Is it because such objects must exist in statements like:

    Point p3 = p1 + p2;

    I know this is a simple point, but I'm confused nonetheless.

    Thanks for any clarification,
    cpp
    cppaddict, Jul 30, 2004
    #1
    1. Advertising

  2. "cppaddict" <> wrote...
    > I'd like to know what goes on under the hood when methods return
    > objects. Eg, I have a simple Point class with two members _x and _y.
    > It's constructor, copy constructor, assignment operator and additon
    > operator (which returns another Point object, and which my question is
    > about) are as follows:
    >
    > Point::point(int x, int y) :
    > _x(x),
    > _y(y) { }
    >
    > Point::point(const Point& p) {
    > _x = p._x;
    > _y = p._y;


    I wonder why you didn't use initialisation here...

    > }
    >
    > Point& Point::eek:perator=(const Point& p) {
    > _x = p._x;
    > _y = p._y;
    > return (*this);
    > }
    >
    > Point Point::eek:perator+(const Point& p) const {
    > Point ret(_x + p._x, _y + p._y);
    > return ret;
    > }
    >
    > What happens when operator+ returns the temporary Point object that it
    > creates?
    >
    > Does it use the copy constructor to create a copy of the temporary
    > variable "ret"? Is ret destroyed after that?


    Generally speaking, yes. Since 'ret' is an automatic variable, it will
    be destroyed right after the function ends (right after the 'return'
    statement). In order for the Point object to exist after the function
    has finished but before its value is used elsewhere, a temporary one
    is created using the copy constructor.

    >
    > If not, how does it return a Point object?
    >
    > Also, why is that "new" never comes into play?


    How do you know it doesn't? Creation of a temporary _might_ be internally
    done using some kind of special form of 'new'. It's not defined, AFAIK.
    Where the temporary is created and how is, well, unimportant. At least
    from the langauge standpoint.

    > How can the compiler
    > know beforehand how many Point objects will be created via operator+?


    Every time it has to call operator+ function, another Point object is
    potentially created.

    > Is it because such objects must exist in statements like:
    >
    > Point p3 = p1 + p2;


    The compiler is allowed to forgo creation of a temporary in this case
    and instead generate code so that when you return the 'ret' from the
    function operator+, it is used to directly initialise the 'p3' object.

    You might find all that in good books. Or by looking at the code (yes,
    it's usually assembly language) that the compiler generates from your
    C++ source.

    Victor
    Victor Bazarov, Jul 30, 2004
    #2
    1. Advertising

  3. cppaddict

    cppaddict Guest


    >> Point::point(const Point& p) {
    >> _x = p._x;
    >> _y = p._y;

    >
    >I wonder why you didn't use initialisation here...


    I should have. Thanks for pointing that out.

    Thanks for you other explanations as well.

    cpp
    cppaddict, Jul 30, 2004
    #3
  4. cppaddict wrote:

    >>>Point::point(const Point& p) {
    >>>_x = p._x;
    >>>_y = p._y;


    it is unwise to use a leading underscore in a variable name, although in
    this case, not technically illegal (17.4.3.1.2).

    mark
    Mark A. Gibbs, Jul 30, 2004
    #4
  5. cppaddict

    cppaddict Guest

    >it is unwise to use a leading underscore in a variable name, although in
    >this case, not technically illegal (17.4.3.1.2).
    >
    >mark


    I was under the (mistaken) impression that this was a standard way to
    indicate member variables. Do you know what leading underscores are
    reserved for? Also, what is the standard way to indicate a member
    variable -- trailing underscore?

    Thanks,
    cpp
    cppaddict, Jul 30, 2004
    #5
  6. John Harrison wrote:
    >
    > On Fri, 30 Jul 2004 03:18:57 GMT, cppaddict <> wrote:
    >
    > >> it is unwise to use a leading underscore in a variable name, although in
    > >> this case, not technically illegal (17.4.3.1.2).
    > >>
    > >> mark

    > >
    > > I was under the (mistaken) impression that this was a standard way to
    > > indicate member variables. Do you know what leading underscores are
    > > reserved for? Also, what is the standard way to indicate a member
    > > variable -- trailing underscore?
    > >
    > > Thanks,
    > > cpp
    > >

    >
    > Its a good way to indicate member variables. I don't understand why some
    > people claim its dangerous (I am aware of when underscore use is illegal).
    > Perhaps Mark could justify his claim?
    >
    > john


    Some say that it's hard to remember the exact rules (never use a leading
    underscore followed by an uppercase letter; never use a double underscore;
    in the global scope, don't use a leading underscore regardless of the
    symbol that follows). There is some potential for an error here,
    especially due to code maintenance by those who don't read too much
    into language details.
    This potential is arguably smaller when using a trailing underscore
    instead.

    I don't mind it either way (and much prefer them over any alternative),
    but in my limited scope the trailing underscore has become more
    conventional. I've got used to it and love it.

    Denis
    Denis Remezov, Jul 30, 2004
    #6
  7. "cppaddict" <> wrote...
    > >it is unwise to use a leading underscore in a variable name, although in
    > >this case, not technically illegal (17.4.3.1.2).
    > >
    > >mark

    >
    > I was under the (mistaken) impression that this was a standard way to
    > indicate member variables. Do you know what leading underscores are
    > reserved for? Also, what is the standard way to indicate a member
    > variable -- trailing underscore?


    There is no "standard way" to indicate anything. The Standard prohibits
    the use of reserved [by the implementation] names. Such names include
    names with double underscores and names that begin with an underscore and
    a capital letter.

    I am, of course, picking at your use of the word "standard" here. Not
    that I have ill will, but traditionally "standard" here means required
    or defined by the Standard Document. You might want to use the word
    "conventional", as in "what is the conventional way to indicate..."

    As to the accepted conventions, there are several. The one I see used
    the most is the prefix 'm_' for non-static data members and 's_' for
    static data members. Leading underscores, just like trailing underscores,
    are not easy to read, that's why most people try to avoid them, I guess.

    Victor
    Victor Bazarov, Jul 30, 2004
    #7
  8. On Fri, 30 Jul 2004 03:18:57 GMT, cppaddict <> wrote:

    >> it is unwise to use a leading underscore in a variable name, although in
    >> this case, not technically illegal (17.4.3.1.2).
    >>
    >> mark

    >
    > I was under the (mistaken) impression that this was a standard way to
    > indicate member variables. Do you know what leading underscores are
    > reserved for? Also, what is the standard way to indicate a member
    > variable -- trailing underscore?
    >
    > Thanks,
    > cpp
    >


    Its a good way to indicate member variables. I don't understand why some
    people claim its dangerous (I am aware of when underscore use is illegal).
    Perhaps Mark could justify his claim?

    john
    John Harrison, Jul 30, 2004
    #8
  9. "Denis Remezov" <> wrote in message
    news:...
    > John Harrison wrote:
    > >
    > > On Fri, 30 Jul 2004 03:18:57 GMT, cppaddict <> wrote:
    > >
    > > >> it is unwise to use a leading underscore in a variable name, although

    in
    > > >> this case, not technically illegal (17.4.3.1.2).
    > > >>
    > > >> mark
    > > >
    > > > I was under the (mistaken) impression that this was a standard way to
    > > > indicate member variables. Do you know what leading underscores are
    > > > reserved for? Also, what is the standard way to indicate a member
    > > > variable -- trailing underscore?
    > > >
    > > > Thanks,
    > > > cpp
    > > >

    > >
    > > Its a good way to indicate member variables. I don't understand why some
    > > people claim its dangerous (I am aware of when underscore use is

    illegal).
    > > Perhaps Mark could justify his claim?
    > >
    > > john

    >
    > Some say that it's hard to remember the exact rules (never use a leading
    > underscore followed by an uppercase letter; never use a double underscore;
    > in the global scope, don't use a leading underscore regardless of the
    > symbol that follows). There is some potential for an error here,
    > especially due to code maintenance by those who don't read too much
    > into language details.
    > This potential is arguably smaller when using a trailing underscore
    > instead.
    >
    > I don't mind it either way (and much prefer them over any alternative),
    > but in my limited scope the trailing underscore has become more
    > conventional. I've got used to it and love it.
    >
    > Denis


    I guess I prefer a leading underscore because its the first thing you see
    when you read a name. I guess the same reason you often see m_something but
    never something_m.

    But yes, beauty is in the eye of the beholder.

    john
    John Harrison, Jul 30, 2004
    #9
  10. cppaddict

    Sharad Kala Guest

    "John Harrison" <> wrote in message
    news:eek:psbxo1yjt212331@andronicus...
    > On Fri, 30 Jul 2004 03:18:57 GMT, cppaddict <> wrote:
    >


    > Its a good way to indicate member variables. I don't understand why some
    > people claim its dangerous (I am aware of when underscore use is illegal).
    > Perhaps Mark could justify his claim?


    Because they are used by the compiler implementations too.
    To quote Herb Sutter here - "Try to avoid names with leading underscores.
    Yes, I've habitually used them, and yes, popular books like "Design
    Patterns" (Gamma et al) do use it... but the standard reserves some
    leading-underscore identifiers for the implementation and the rules are hard
    enough to remember (for you and for compiler writers!) that you might as
    well avoid this in new code. (Since I'm no longer allowed to use leading
    underscores as my "member variable" tag, I'll now use trailing
    underscores!)"

    This simple program on MS VC 7.0 cribs for ambiguous symbol -

    #include <map>
    using namespace std;
    class _Tree{
    };

    int main()
    {
    _Tree t;
    }

    -Sharad
    Sharad Kala, Jul 30, 2004
    #10
  11. cppaddict

    JKop Guest

    cppaddict posted:

    > I'd like to know what goes on under the hood when methods return
    > objects. Eg, I have a simple Point class with two members _x and _y.
    > It's constructor, copy constructor, assignment operator and additon
    > operator (which returns another Point object, and which my question is
    > about) are as follows:
    >
    > Point::point(int x, int y) :
    > _x(x),
    > _y(y) { }
    >
    > Point::point(const Point& p) {
    > _x = p._x;
    > _y = p._y;
    > }
    >
    > Point& Point::eek:perator=(const Point& p) {
    > _x = p._x;
    > _y = p._y;
    > return (*this);
    > }
    >
    > Point Point::eek:perator+(const Point& p) const {
    > Point ret(_x + p._x, _y + p._y);
    > return ret;
    > }
    >
    > What happens when operator+ returns the temporary Point object that it
    > creates?
    >
    > Does it use the copy constructor to create a copy of the temporary
    > variable "ret"? Is ret destroyed after that?
    >
    > If not, how does it return a Point object?
    >
    > Also, why is that "new" never comes into play? How can the compiler
    > know beforehand how many Point objects will be created via operator+?
    > Is it because such objects must exist in statements like:
    >
    > Point p3 = p1 + p2;
    >
    > I know this is a simple point, but I'm confused nonetheless.
    >
    > Thanks for any clarification,
    > cpp
    >



    int Blah()
    {
    return 72;
    }

    int main()
    {
    int f = 45 + Blah();
    }


    When the end of Blah is reached, a temporary is created and this temporary
    is given back to main. The temporary lives in main up until the next
    semicolon, at which point it's destroyed.

    Now, here's one that may result in an optimization:

    int Blah()
    {
    int f = 76;

    f *= 2;

    f -= 4;

    return f;
    }

    The compiler may choose to return a temporary, which has been copy-
    initialized from "f", or it may return "f" itself. If it returns "f" itself,
    then there's one less object created, and that's the optimization.

    And another optimization:

    int Blah()
    {
    int f = 76;

    f *= 2;

    f -= 4;

    return f;
    }

    int main()
    {
    int x = Blah();
    }

    That x variable in main may be used in Blah, as no temporary is necessary.


    And then there's functions that return references...

    int k; //global

    int& Blah()
    {
    k = 6783;

    k *= 2;

    k -= 64;

    return k;
    }

    Here Blah is returning a reference to a global variable, so the global
    variable will still exist after the end of Blah, and so it can be used in
    main().

    If, on the other hand, you do this:

    int& Blah()
    {
    int k = 72;

    return k;
    }

    The Blah function will in fact return a reference to k, but by the time you
    get to use it in main, k no longer exists. Compilers warn whenever you
    return a reference to a local variable.


    And then there's binding to a const-reference:

    int Blah()
    {
    int k = 72;

    k +=4;

    k *= 2;

    return k;
    }

    int main()
    {
    const int& monkey = Blah();

    //Here, the temporary returned from Blah (or its local variable)
    //is directly bound to monkey
    //The temporary does NOT get destroyed at the
    //next semicolon, but at the end of the reference's
    //scope, ie. the end of main
    }


    You may ask why one would bother binding to a const reference when they can
    just do:

    int main()
    {
    int monkey = Blah();
    }

    and rely on the optimization.

    Well, there needn't be an optimization depending on the compiler, and so
    monkey may be copy-constructed from the temporary returned by Blah(). Plus,
    if Blah returns a const object, then "int monkey" won't optimize, because a
    copy will have to be made to yield a non-const version. On the other hand
    you could do "const int monkey".

    Well anyway, take this code:

    int Blah()
    {
    int k = 42;

    k *= 2;

    k += 4;

    return k;
    }

    int main()
    {
    int monkey = Blah();
    }


    How many "int"s are made? Either 1, 2 or 3. The best compilers will only use
    one int, the monkey from main. The monkey will be used in Blah, and then
    when Blah finishes, a temporary *won't* be made, it'll just return its local
    variable, which is monkey from main!

    Another compiler may make 2 "int"s: the monkey in main, and the k in Blah.
    The Blah function will return k directly.

    And then ofcourse there's compilers that'll make 3 "int"s:

    The monkey from main
    k from Blah
    The temporary returned from Blah

    There's times when a function has no choice but to create a temporary to
    return, eg.

    int Blah()
    {
    int k = 7;

    return k + 4;
    }

    But if you return the variable directly, and there's no constness problems,
    then there's no problems with the actual local variable being returned.

    All of these are just optimizations, and needn't be performed.

    (Another even better compiler may turn the above function into:

    int Blah()
    {
    int k = 7;

    return k += 4;
    }

    And return "k" itself, as it can see that there's no consequences of adding
    the 4 to k directly. One less temporary.


    -JKop
    JKop, Jul 30, 2004
    #11
  12. "Sharad Kala" <> wrote in message
    news:...
    >
    > "John Harrison" <> wrote in message
    > news:eek:psbxo1yjt212331@andronicus...
    > > On Fri, 30 Jul 2004 03:18:57 GMT, cppaddict <> wrote:
    > >

    >
    > > Its a good way to indicate member variables. I don't understand why some
    > > people claim its dangerous (I am aware of when underscore use is

    illegal).
    > > Perhaps Mark could justify his claim?

    >
    > Because they are used by the compiler implementations too.
    > To quote Herb Sutter here - "Try to avoid names with leading underscores.
    > Yes, I've habitually used them, and yes, popular books like "Design
    > Patterns" (Gamma et al) do use it... but the standard reserves some
    > leading-underscore identifiers for the implementation and the rules are

    hard
    > enough to remember (for you and for compiler writers!) that you might as
    > well avoid this in new code. (Since I'm no longer allowed to use leading
    > underscores as my "member variable" tag, I'll now use trailing
    > underscores!)"


    I don't find the rule, 'use a leading underscore followed by a lower case
    letter for member variables' hard to remember. I would always use a leading
    lower case letter in any case even if I wasn't using a leading underscore. I
    don't think the argument that you 'might as well' avoid it in new code very
    convincing. Why exactly? No bad things are going to happen that I can see.

    >
    > This simple program on MS VC 7.0 cribs for ambiguous symbol -
    >
    > #include <map>
    > using namespace std;
    > class _Tree{
    > };
    >
    > int main()
    > {
    > _Tree t;
    > }
    >
    > -Sharad
    >


    That differs from what I would do in two different ways. First it is
    underscore followed by an uppercase letter and second I am only talking
    about member variable names.

    john
    John Harrison, Jul 30, 2004
    #12
  13. cppaddict

    cppaddict Guest

    JKop,

    Thanks for that discussion... very interesting.

    cpp
    cppaddict, Jul 30, 2004
    #13
  14. cppaddict wrote:
    >
    > Point Point::eek:perator+(const Point& p) const {
    > Point ret(_x + p._x, _y + p._y);
    > return ret;
    > }


    I would prefer having binary operators as non-member functions, like
    this:

    Point operator+(const Point& p1, const Point& p2) {
    Point ret(p1._x + p2._x, p1._y + p2._y);
    return ret;
    }


    What do you think?

    Niels Dekker
    www.xs4all.nl/~nd/dekkerware
    Niels Dekker (no reply address), Jul 30, 2004
    #14
  15. In message <>, "Niels Dekker (no reply
    address)" <> writes
    >cppaddict wrote:
    >>
    >> Point Point::eek:perator+(const Point& p) const {
    >> Point ret(_x + p._x, _y + p._y);
    >> return ret;
    >> }

    >
    >I would prefer having binary operators as non-member functions, like
    >this:
    >
    >Point operator+(const Point& p1, const Point& p2) {
    > Point ret(p1._x + p2._x, p1._y + p2._y);
    > return ret;
    >}
    >
    >
    >What do you think?


    I think I'd implement non-member operator+() in terms of member
    operator+=().


    --
    Richard Herring
    Richard Herring, Jul 30, 2004
    #15
  16. cppaddict wrote:
    >
    > Point Point::eek:perator+(const Point& p) const {
    > Point ret(_x + p._x, _y + p._y);
    > return ret;
    > }


    I wrote:
    >
    > I would prefer having binary operators as non-member functions [...]



    Richard Herring wrote:
    >
    > I think I'd implement non-member operator+() in terms of member
    > operator+=().



    So I guess you mean like this:

    Point& Point::eek:perator+=(const Point& p) {
    _x += p._x;
    _y += p._y;
    return *this;
    }

    Point operator+(const Point& p1, const Point& p2) {
    return Point(p1) += p2;
    }


    Would you write other binary operators, like operator!=, as non-members
    as well? In what situation would you prefer to implement a binary
    operator as a member function?

    Thanks in advance,

    Niels Dekker
    www.xs4all.nl/~nd/dekkerware
    Niels Dekker (no reply address), Jul 30, 2004
    #16
  17. In message <>, "Niels Dekker (no reply
    address)" <> writes
    >cppaddict wrote:
    >>
    >> Point Point::eek:perator+(const Point& p) const {
    >> Point ret(_x + p._x, _y + p._y);
    >> return ret;
    >> }

    >
    >I wrote:
    >>
    >> I would prefer having binary operators as non-member functions [...]

    >
    >
    >Richard Herring wrote:
    >>
    >> I think I'd implement non-member operator+() in terms of member
    >> operator+=().

    >
    >
    >So I guess you mean like this:
    >
    >Point& Point::eek:perator+=(const Point& p) {
    > _x += p._x;
    > _y += p._y;
    > return *this;
    >}
    >
    >Point operator+(const Point& p1, const Point& p2) {
    > return Point(p1) += p2;
    >}
    >

    Exactly.

    >
    >Would you write other binary operators, like operator!=, as non-members
    >as well?


    Yes, if both their operands are const and of the same type. That way,
    you get the same rules for automatic type conversions applied to both
    operands. With member functions, the first operand is treated
    differently, with the possibility that a op b and b op a produce
    different conversions if a and b are of different (but convertible)
    types.

    > In what situation would you prefer to implement a binary
    >operator as a member function?
    >

    If the function modifies its argument (e.g. operator+= above.).

    --
    Richard Herring
    Richard Herring, Jul 30, 2004
    #17
  18. Richard Herring wrote:
    > In message <>, "Niels Dekker (no reply
    > [...]
    >> In what situation would you prefer to implement a binary
    >> operator as a member function?
    >>

    > If the function modifies its argument (e.g. operator+= above.).


    Operator += is an assignment operator and it cannot be implemented as
    a non-member, no matter whether it in fact modifies its left operand
    or not (and it is up to you to decide whether it does modify it).

    There are operators that can be made non-members and there are others
    that cannot. For example, type conversion operators must be members
    because such is the requirement of the language. They most likely do
    not modify their operand, however.

    V
    Victor Bazarov, Jul 30, 2004
    #18
  19. cppaddict wrote:
    >
    > JKop,
    >
    > Thanks for that discussion... very interesting.
    >
    > cpp


    Replace int with a user-defined type and what JKop wrote will
    probably make sense (I haven't checked it closely).

    In respect to type int, his story may still be "as-if"-correct
    but is actually misleading in a practical sense. Very crudely
    put, type int is small, cheap to copy, and is typically subject
    to machine treatment not generally available to larger-sized
    user-defined types.

    For example, more often than not,
    int const& f(int const& x) {...}

    will be the same speed (if the compiler optimises it) as or a tiny
    bit slower (because of indirection) than
    int f(int x) {...}

    Not that you should worry too much about that, but you can see
    for yourself how things work by using your debugger to step
    through the assembly code (optimised and unoptimised).

    Denis
    Denis Remezov, Jul 30, 2004
    #19
  20. Victor Bazarov wrote in news:VZsOc.248$191.84
    @newsread1.dllstx09.us.to.verio.net in comp.lang.c++:

    > Richard Herring wrote:
    >> In message <>, "Niels Dekker (no reply
    >> [...]
    >>> In what situation would you prefer to implement a binary
    >>> operator as a member function?
    >>>

    >> If the function modifies its argument (e.g. operator+= above.).

    >
    > Operator += is an assignment operator and it cannot be implemented as
    > a non-member, no matter whether it in fact modifies its left operand
    > or not (and it is up to you to decide whether it does modify it).
    >


    #include <iostream>
    #include <ostream>

    struct X {};

    X &operator += ( X &lhs, X const & )
    {
    std::cout << "Ok" << std::endl;
    return lhs;
    }

    int main()
    {
    X a, b;
    a += b;
    }

    Works fine for me.

    > There are operators that can be made non-members and there are others
    > that cannot. For example, type conversion operators must be members
    > because such is the requirement of the language. They most likely do
    > not modify their operand, however.


    Also operator ->(), I can't remember any others (*).

    *) Ok, new and delete, but I don't think they count :).

    Rob.
    --
    http://www.victim-prime.dsl.pipex.com/
    Rob Williscroft, Jul 30, 2004
    #20
    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. Greenhorn
    Replies:
    15
    Views:
    781
    Keith Thompson
    Mar 6, 2005
  2. 7stud
    Replies:
    11
    Views:
    662
    Dennis Lee Bieber
    Mar 20, 2007
  3. Felix T.
    Replies:
    1
    Views:
    241
    Arnaud Delobelle
    Nov 19, 2008
  4. Kenneth McDonald
    Replies:
    5
    Views:
    289
    Kenneth McDonald
    Sep 26, 2008
  5. Replies:
    8
    Views:
    284
    Jorgen Grahn
    May 16, 2013
Loading...

Share This Page