Functions which return a reference

This is a discussion on Functions which return a reference within the C++ Programming forums, part of the General Programming Boards category; I can't seem to get my head around functions which return a reference, what is it a reference to? It ...

  1. #1
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118

    Functions which return a reference

    I can't seem to get my head around functions which return a reference, what is it a reference to? It seems they are commonly used when writing operators. Here's a couple of examples I've found.

    The first is the assignment operator from Steve Heller's string class

    Code:
    string& string::operator = (const string& Str)
    {
    char* temp = new char[Str.m_Length];
    
    m_Length = Str.m_Length;
    memcpy(temp,Str.m_Data,m_Length);
    delete [ ] m_Data;
    
    m_Data = temp;
    
    return *this;
    }
    The second is taken from this well known pointer tutorial

    Code:
    class array {
     4   int base[5];
     5 public:
     6   int& operator[] ( int i )
     7   {
     8     return base[i];
     9   }
    10 };
    In both cases I just can't see why we need to return a reference, or even what it's a reference to.

    Regards,

    Stonehambey

  2. #2
    C++まいる!Cをこわせ! Elysia's Avatar
    Join Date
    Oct 2007
    Posts
    22,782
    The first example returns *this, which essentially means it is returning a reference to itself.
    This is to allow syntax such as:

    std::string s, s2, s3;
    s = s2 = s3 = "blah"

    The compiler will call the operator = const char* for s3, which would return a reference to itself, which would then be passed to s2, and after that, s2 is passed to s.
    If you would return void, this would not be possible. Of course, the reason we return a reference to itself is so that that actual object will be assigned to the next object in line.

    The second example returns a reference to the private base object.
    Why is it doing this? It's to emulate an array. It simply returns a reference to the object at index i.
    After invoking the index operator, it's usually possible to assign, for example. By returning a reference, the compiler can then assign to that value:

    array arr;
    arr[5] = 10;

    First it invokes operator [], which returns a reference to the item in question. Then the compiler calls operator = on that reference, completing the cycle.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118
    Thanks for the reply.

    I'm still unsure how the program would fail to work properly if the assignment operator function returned string instead of string&. Say it returned just string, then in the example

    s = s2 = s3 = "blah"

    the first assignment would return a string, then the second assignment would be invoked, which has a reference in the argument. Doesn't that tell the compiler that we want to use a reference of the string class? I might not be sounding clear, maybe this will help, say I have a function

    Code:
    void f( int& ) 
    {
        //do something with the int
    }
    Now this function can be called like this

    Code:
    int i = 1;
    
    f(i);

    But we don't need to specifically pass a reference to the function, we can just pass i, because the function has a reference in the argument then it knows that's what we want.

    So we don't need to do something like this

    Code:
    int i = 1;
    int& j = i;    //make reference of i
    
    f(j);   //specifically pass that reference as the argument.
    So to sum up, even if the string operator returned a string, then if we wanted to invoke the assignment operator again using the returned string, I don't see how it would matter since the argument for the operator knows we want to use it as a reference anyway.

    I have no doubt that I am missing something, as here's another quote from the article

    By returning a reference, two nifty tricks are allowed. First, you can return a reference and chain calls of a function together as long as they take that reference as an argument:
    I just can't see what I'm missing

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,914
    So to sum up, even if the string operator returned a string, then if we wanted to invoke the assignment operator again using the returned string, I don't see how it would matter since the argument for the operator knows we want to use it as a reference anyway.
    To illustrate the difference, run this test program:
    Code:
    #include <iostream>
    
    class X
    {
    public:
        X()
        {
            std::cout << "Default ctor called." << std::endl;
        }
    
        X(const X& other)
        {
            std::cout << "Copy ctor called." << std::endl;
        }
    
        X& operator=(const X& other)
        {
            std::cout << "Copy assignment operator called." << std::endl;
            return *this;
        }
    
        ~X()
        {
            std::cout << "Dtor called." << std::endl;
        }
    };
    
    int main()
    {
        X a, b, c;
        a = b = c;
    }
    Now, edit the copy assignment operator to make it return by value instead of by reference, then run the program again and observe the difference.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118
    Hey thanks for that, I think I'm beginning to understand it a bit now, but still a little shaky. Here's what the program shows.

    Default ctor called.
    Default ctor called.
    Default ctor called.
    Copy assingment operator called.
    Copy assingment operator called.
    Dtor called.
    Dtor called.
    Dtor called.

    I get this fine. You create 3 instances of the class, invoking the default constructor. You then perform two assingments, and finally the default constructor is called for each of them.

    So I change the program as you suggest and I see this


    Default ctor called.
    Default ctor called.
    Default ctor called.
    Copy assingment operator called.
    Copy ctor called.
    Copy assingment operator called.
    Copy ctor called.
    Dtor called.
    Dtor called.
    Dtor called.
    Dtor called.
    Dtor called.

    So now after the copy assignment operators are called we are also somehow invoking the copy constructor as well.

    So could it be that when if we return simply X in our assignment operator, then we are creating a new instance of the class created by the function (sounds reasonable)? And so we need a constructor, since the arguments for operator= and the copy constructor are the same, it induces the copy constructor?

    Also, what does the reference returned actually reference? We return *this, which by definition is a dereferenced pointer to this instance of the class. So by declaring the function returns a reference are we saying "return a reference to this".

    Sorry if I'm confusing everyone, maybe I'm having a slow day today or something, for some reason this isn't quite clicking for me yet

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,914
    So could it be that when if we return simply X in our assignment operator, then we are creating a new instance of the class created by the function (sounds reasonable)?
    Yes.

    And so we need a constructor, since the arguments for operator= and the copy constructor are the same, it induces the copy constructor?
    I am not sure what you mean, but the act of returning by value invokes the copy constructor, at least if return value optimisation is not applied.

    Also, what does the reference returned actually reference? We return *this, which by definition is a dereferenced pointer to this instance of the class. So by declaring the function returns a reference are we saying "return a reference to this".
    It refers to the current object itself.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118
    Quote Originally Posted by laserlight View Post
    I am not sure what you mean, but the act of returning by value invokes the copy constructor, at least if return value optimisation is not applied.
    I just mean that in order to make this new object, the program needs to use a constructor. So presumably it goes through its list of constructors and picks the right one. I'm wondering why it picks the copy constructor. My initial reasoning was that it shares the same arguments as the copy assignment operator.

    It refers to the current object itself.
    Ok cool, so would the following function return a reference to a?

    Code:
    int& f(int a)
    {
        return a;
    }

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,914
    I just mean that in order to make this new object, the program needs to use a constructor. So presumably it goes through its list of constructors and picks the right one. I'm wondering why it picks the copy constructor. My initial reasoning was that it shares the same arguments as the copy assignment operator.
    Because a copy of the object is returned. The parameters of the copy assignment operator have nothing to do with it. Only the return type of the copy assignment operator matters here.

    Ok cool, so would the following function return a reference to a?
    An integer that no longer exists since it goes out of scope once control leaves the function. This is a Bad Thing. Returning a reference to *this on the other hand is perfectly fine since the object continues to exist after control leaves its member function.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118
    I think it's slowly starting to fall into place now, thanks for your patience.

    With regards to your last comment. What difference would this make? Comments are just me thinking out loud

    Code:
    int& f(int& x) //pass a reference to an integer x as the argument. 
    {
        return x; //function now deals with x rather than a copy of x (so exists outside
                       //of the function scope), so it's ok to return it as a 
                       // reference?
    }
    Are there any general rules for when one should return a reference?

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,914
    What difference would this make?
    Your reasoning is correct. This is now okay since x presumably exists in the caller.

    EDIT:
    Are there any general rules for when one should return a reference?
    You might want to read this FAQ on Reference and value semantics. Note that what it means by "reference semantics" also concerns pointers, though pointers are themselves passed by value.

    Anyway, as you have seen in the case of the copy assignment operator, returning by reference is often used to implement operator chaining. Another example of this is the overloaded operator<< and operator>> for std::ostream and std::istream respectively. More generally, this can be used to chain functions that are not operators as well.
    Last edited by laserlight; 07-05-2008 at 01:33 PM.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User Stonehambey's Avatar
    Join Date
    Jan 2008
    Location
    Kent, UK
    Posts
    118

    Smile

    That looks like a pretty cool FAQ, and thanks for the help

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  2. 6 measly errors
    By beene in forum Game Programming
    Replies: 11
    Last Post: 11-14-2006, 10:06 AM
  3. Linking OpenGL in Dev-C++
    By linkofazeroth in forum Game Programming
    Replies: 4
    Last Post: 09-13-2005, 10:17 AM
  4. How to: Use OpenGL with Jgrasp
    By Pickels in forum Game Programming
    Replies: 3
    Last Post: 08-30-2005, 10:37 AM
  5. Certain functions
    By Lurker in forum C++ Programming
    Replies: 3
    Last Post: 12-26-2003, 12:26 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21