7

I have the following class interface:

  class Time
  {
  public:
     Time( int = 0, int = 0, int = 0 ); 
     Time &setHour( int );                 
     Time &setMinute( int );               
     Time &setSecond( int ); 

  private:
     int hour; 
     int minute; 
     int second; 
  }; 

The implementation is here:

  Time &Time::setHour( int h ) 
  {
     hour = ( h >= 0 && h < 24 ) ? h : 0; 
     return *this; 
  } 


  Time &Time::setMinute( int m ) 
  {
     minute = ( m >= 0 && m < 60 ) ? m : 0; 
     return *this; 
  } 


  Time &Time::setSecond( int s ) 
  {
     second = ( s >= 0 && s < 60 ) ? s : 0; 
    return *this; 
   }

In my main .cpp file, I have this code:

int main()
{
    Time t;     
    t.setHour( 18 ).setMinute( 30 ).setSecond( 22 );
    return 0;
}

How is it possible to chain these function calls together? I don't understand why this works.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
teacher
  • 1,005
  • 3
  • 15
  • 27

7 Answers7

15

The reason that this works correctly is that when you call

t.setHour( 18 )

The return value is a Time&, a reference to a Time object. More importantly, it's defined as

Time &Time::setHour( int h ) 
{
   hour = ( h >= 0 && h < 24 ) ? h : 0; 
   return *this;  // <--- right here
}

Inside of a member function, this is a pointer to the object on which the call was made, and *this is a reference to the object on which the call was made (the receiver object). This means that when you call setHour, the function sets the hour on the time, then returns a reference to the Time object on which you made the call. Thus t.setHour( 18 ) both sets the hour and then returns a reference to the receiver object. That way, you can write

t.setHour( 18 ).setMinute( 30 ).setSecond( 22 );

because it's interpreted as

((t.setHour( 18 )).setMinute( 30 )).setSecond( 22 );

and in each case the function returns a reference to t.

More generally, any time a function returns a reference and that reference is *this, any operation you perform on the return value of the function is indistinguishable from operations that you would perform on the object itself.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • In function call, If I want to use arrow operator,instead of dot operator, what should be the function return?? (Ex) t.setHour(18)->setMinute(30)->setSecond(22) – usmanharoon Nov 18 '21 at 05:33
  • 1
    You’d return a pointer to the object, which would have type `Time*` in the example. The actual return statement would be `return this;`. – templatetypedef Nov 18 '21 at 15:55
11

Each of t's methods return a reference to t. A reference is an alias. So if you did

 Time t;
 Time& tAgain = t;
 tAgain.setMinute( 30 ); 

tAgain.setMinute also alters t's time.

Now extrapolate that simple example to cascading. Each method of t returns a reference to itself

  Time &Time::setSecond( int s ) 
  {
      second = ( s >= 0 && s < 60 ) ? s : 0; 
      return *this; 
  }

So in the expression:

  t.setHour( 18 ).setMinute( 30 )

t.setHour( 18 ) calls setHour on t, then returns a reference to t. In this case the reference is temporary. So you can think of it as if the above line changed to the following on evaluating setHour:

  tAgain.setMinute(30);

t.setHour returned a reference -- similar to our tAgain above. Just an alias for t itself.

Doug T.
  • 64,223
  • 27
  • 138
  • 202
6

Due to the fact that each function returns a reference to the this object object (The return *this).

Basically this means that every time the function is called it make the relevant changes and then passes the entire object back out as a reference. It is then possible to make calls on that returned object.

It could also be written as follows:

 Time t;
 Time& t1 = t.setHour( 18 ); // t1 will refer to the same object as t.
 Time& t2 = t1.setMinute( 30 ); // t2 will refer to the same object as t1 and t.
 Time& t3 = t2.setSecond( 22 ); // t3 will refer to the same object as t2, t1 and t.

That may make it easier to understand what is going on.

Goz
  • 61,365
  • 24
  • 124
  • 204
  • oh i got it.. `t.setHour( 18 )` will leave `(*this)` which will be used to reference next function... – teacher Aug 30 '11 at 21:18
3

This is similar to overloading stream operators.

ostream& operator<<(ostream& s, const T& val)
{
    s << val;
    return s;
}

You do this because you modify the stream and return it so it can be used in the next cascading call if desired. It keeps getting passed by reference so it can keep going into the next expression segment.

Thats how:

std::cerr << 1 << 2 << 3 << std::endl;

works! :)

John Humphreys
  • 37,047
  • 37
  • 155
  • 255
1

because when a function is executed and returns then it returns reference to itself so again it can call functions.

1

The technique is called method chaining. In the example you've given, all the methods return the same object (this), so they all affect the same object. That's not uncommon, but it's useful to know that it doesn't have to be the case; some or all of the methods in the chain can return different objects. For example, you might also have methods like:

Date Time::date() const;
String Date::dayOfWeek() const;

in which case you could say:

Time t;
String day = t.date().dayOfWeek();

to get the name of day of the week. In that case, t.date() returns a Date object which is used in turn to call dayOfWeek().

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • +1 just for first phrase, -1 for not saying `Date Time::date() const;` and using a redundant void. Therefore, +-0. edit: After editing your answer, I'll +1 it. – Sebastian Mach Sep 27 '11 at 07:15
  • @phresnel, That's one way to do it, but different from what I intended. Why return a new object rather than a reference to the existing one? Returning a reference to an existing object seems equally valid, as demonstrated [here](http://en.wikipedia.org/wiki/Method_chaining) and [here](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.20). It also seems more efficient and more natural when you consider that chaining is often used to configure a new object. – Caleb Sep 27 '11 at 07:27
  • Okay, I thought those were typos. Are you sure that `dayOfWeek()` is ought to return a reference to a non-const string? Why the redundant `void`s? Also, those examples return `*this`, and not instances of another type, which can very subtly introduce dangerous bugs of the sort that possibly unhides just before your customer. – Sebastian Mach Sep 27 '11 at 07:48
  • @phresnel, Doh! I was thinking more about your edit than the point I was trying to make. Points in my comment above are covered by the code in the question, and with this answer I was trying point out that you can chain without necessarily returning `this` (or `*this`). Your edits help in that respect. I could go either way on the `f(void)`; it's mostly a habit from C and I like it because it's explicit. I don't think it's incorrect, but it's probably not as common as `f()`. – Caleb Sep 27 '11 at 08:02
  • A common bug is something like `stringstream ss; ... foo (ss.str().c_str());`, where you can think of `c_str()` returning a reference, too, but because `str()` returns a temporary object, calling `c_str()` compiles, but is undefined behaviour. This is the sort of bug that can also easily happen with non-const references (though I am also sure you already know this :) ). +1 revived again. – Sebastian Mach Sep 27 '11 at 08:15
1

It might help if you think of the statements being solved one step at a time.

Take the following for instance:

x = 1 + 2 * 3 - 4;
x = 1 + 6 - 4;
x = 7 - 4;
x = 3;

C++ does the same with function calls and everything else you do within a statement, solving each element inside in the order of operator precedence. So you can think of your example as being solved in the same way:

t.setHour( 18 ).setMinute( 30 ).setSecond( 22 );
t.setMinute( 30 ).setSecond( 22 );  // hour is now set to 18
t.setSecond( 22 ); // minute is now set to 30
t; // seconds now set to 22

If you returned this instead of *this, and thus returning pointers instead of references, you would get the same effect except you would replace the . with -> (just as an example, you're doing it right by using references). In the same way, if you returned an pointer or reference to a different object, you could do the same thing with that. For instance, let's say you have a function that returns a Time object.

class Time{
    public:
    int getSeconds(){
        return seconds;
    };
    int seconds;
};

Time getCurrentTime(){
    Time time = doSomethingThatGetsTheTime();
    return time;
};

int seconds = getCurrentTime().getSeconds();    

You get the seconds without having to split the statement into two different statements or making a temporary variable to hold the returned Time object.

This question C++: Using '.' operator on expressions and function calls goes a little more indepth if you wanted to read.

Community
  • 1
  • 1
Anne Quinn
  • 12,609
  • 8
  • 54
  • 101