7

我有以下类接口:

  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; 
  }; 

实现在这里:

  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; 
   }

在我的主 .cpp 文件中,我有以下代码:

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

如何将这些函数调用链接在一起?我不明白为什么会这样。

4

7 回答 7

14

这可以正常工作的原因是当你打电话时

t.setHour( 18 )

返回值是 a Time&,一个Time对象的引用。更重要的是,它被定义为

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

在成员函数内部,this是一个指向调用对象的指针,并且*this是对调用对象(接收者对象)的引用。这意味着当您调用 时setHour,该函数会设置小时,然后返回Time对您进行调用的对象的引用。因此t.setHour( 18 ),两者都设置小时,然后返回对接收器对象的引用。这样,你可以写

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

因为它被解释为

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

并且在每种情况下,该函数都返回对t.

更一般地,任何时候函数返回一个引用并且该引用是*this,您对函数的返回值执行的任何操作都与您对对象本身执行的操作没有区别。

希望这可以帮助!

于 2011-08-30T21:18:16.610 回答
10

t 的每个方法都返回对 t 的引用。引用是别名。所以如果你这样做了

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

tAgain.setMinute也改变了时间。

现在将这个简单的例子外推到级联。t 的每个方法都返回对自身的引用

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

所以在表达式中:

  t.setHour( 18 ).setMinute( 30 )

t.setHour( 18 )在 t 上调用 setHour,然后返回对 t 的引用。在这种情况下,引用是临时的。因此,您可以将其视为在评估 setHour 时上述行更改为以下内容:

  tAgain.setMinute(30);

t.setHour 返回了一个引用——类似于我们上面的 tAgain。只是 t 本身的别名。

于 2011-08-30T21:20:09.823 回答
6

由于每个函数都返回对 this 对象对象的引用(返回 *this)。

基本上,这意味着每次调用该函数时都会进行相关更改,然后将整个对象作为参考传递出去。然后可以对该返回的对象进行调用。

也可以这样写:

 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.

这可能更容易理解正在发生的事情。

于 2011-08-30T21:17:02.257 回答
3

这类似于重载流运算符。

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

您这样做是因为您修改了流并将其返回,以便在需要时可以在下一个级联调用中使用它。它不断通过引用传递,因此它可以继续进入下一个表达式段。

这就是如何:

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

作品!:)

于 2011-08-30T21:19:24.537 回答
1

该技术称为方法链接。在您给出的示例中,所有方法都返回同一个对象(this),因此它们都影响同一个对象。这并不少见,但知道不一定是这样是很有用的。链中的部分或全部方法可以返回不同的对象。例如,您可能还有以下方法:

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

在这种情况下,您可以说:

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

获取星期几的名称。在这种情况下,t.date()返回一个 Date 对象,该对象依次用于调用dayOfWeek().

于 2011-08-30T21:39:20.477 回答
1

如果您认为陈述是一步一步解决的,这可能会有所帮助。

以以下为例:

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

C++ 对函数调用和您在语句中执行的所有其他操作执行相同的操作,按照运算符优先级的顺序解决内部的每个元素。因此,您可以将您的示例视为以相同的方式解决:

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

如果您返回this而不是*this,因此返回指针而不是引用,您将获得相同的效果,除非您将替换为.with ->(例如,您通过使用引用正确地做到了这一点)。同样,如果您返回一个指针或对不同对象的引用,您可以用它做同样的事情。例如,假设您有一个返回Time对象的函数。

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

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

int seconds = getCurrentTime().getSeconds();    

您无需将语句拆分为两个不同的语句或创建一个临时变量来保存返回的 Time 对象即可获得秒数。

这个问题C++: Using '.' 如果您想阅读,表达式和函数调用上的运算符会更深入一些。

于 2011-08-30T23:33:48.353 回答
1

因为当一个函数被执行并返回时,它会返回对自身的引用,所以它可以再次调用functions.

于 2012-08-29T13:53:46.643 回答