我有以下类接口:
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;
}
如何将这些函数调用链接在一起?我不明白为什么会这样。
答案 0 :(得分:14)
这可以正常工作的原因是当你打电话
t.setHour( 18 )
返回值是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
,您对函数返回值执行的任何操作都与您在对象本身上执行的操作无法区分。
希望这有帮助!
答案 1 :(得分:6)
由于每个函数都返回对此对象对象的引用(返回* 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.
这可能会让您更容易理解正在发生的事情。
答案 2 :(得分:6)
每种方法都会将引用返回给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);
.setHour返回了一个引用 - 类似于上面的tAgain。只是t本身的别名。
答案 3 :(得分:3)
这类似于重载流操作符。
ostream& operator<<(ostream& s, const T& val)
{
s << val;
return s;
}
您这样做是因为您修改了流并将其返回,以便在需要时可以在下一个级联调用中使用它。它不断通过引用传递,因此它可以继续进入下一个表达式段。
多数民众赞成:
std::cerr << 1 << 2 << 3 << std::endl;
作品! :)
答案 4 :(得分:1)
该技术称为method chaining。在您给出的示例中,所有方法都返回相同的对象(this),因此它们都会影响同一个对象。这并不罕见,但知道不一定是这样的,这很有用;链中的部分或全部方法可以返回不同的对象。例如,您可能还有以下方法:
Date Time::date() const;
String Date::dayOfWeek() const;
在这种情况下你可以说:
Time t;
String day = t.date().dayOfWeek();
获取星期几的名称。在这种情况下,t.date()
会返回一个Date对象,该对象又用于调用dayOfWeek()
。
答案 5 :(得分: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
,从而返回指针而不是引用,那么您将获得相同的效果,除非您将.
替换为->
(举个例子,你是通过使用引用来做到的。以同样的方式,如果您返回指向不同对象的指针或引用,则可以对此执行相同的操作。例如,假设您有一个返回Time
对象的函数。
class Time{
public:
int getSeconds(){
return seconds;
};
int seconds;
};
Time getCurrentTime(){
Time time = doSomethingThatGetsTheTime();
return time;
};
int seconds = getCurrentTime().getSeconds();
您可以获得秒数,而无需将语句拆分为两个不同的语句,或者创建一个临时变量来保存返回的Time对象。
如果你想阅读,这个问题C++: Using '.' operator on expressions and function calls会更加深入。
答案 6 :(得分:1)
因为当一个函数被执行并返回时它会返回对它自己的引用,所以它再次可以调用functions
。