2011-08-30 36 views
6

我有以下的類接口:「這個」級聯如何工作?

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

怎麼回事可能將這些函數調用鏈接在一起?我不明白爲什麼這會起作用。

回答

5

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) 

​​上噸呼叫setHour,然後返回至t的參考。在這種情況下,參考是暫時的。這樣,如果上述線路改爲在評估setHour以下你可以把它想象:

tAgain.setMinute(30); 

t.setHour返回的參考 - 類似於上面我們tAgain。只是t本身的別名。

5

由於每個函數都返回對此對象對象的引用(返回* 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. 

這可能使其更易於理解是怎麼回事。

+0

哦,我明白了......'t.setHour(18)'會留下'(* this)',它將被用來引用下一個函數...... – teacher

+1

@teacher:的確如此。 – Goz

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)).setMinute(30)).setSecond(22); 

,並在函數返回t參考每種情況下寫

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

更一般地說,任何時候一個函數返回一個引用,並且該引用是*this,那麼您對該函數的返回值執行的任何操作都與您將在該對象本身上執行的操作無法區分。

希望這會有所幫助!

3

這與重載流操作符類似。

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

您這樣做是因爲您修改了流並將其返回,以便在需要時可用於下一個級聯調用。它不斷通過引用傳遞,所以它可以繼續進入下一個表達式段。

那如何:

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

作品! :)

+0

這看起來更像是對我的評論 – sehe

1

該技術被稱爲method chaining。在你給出的例子中,所有的方法都返回同一個對象(this),所以它們都影響同一個對象。這並不罕見,但知道它不一定是這樣的;鏈中的一些或全部方法可以返回不同的對象。例如,你可能也有這樣的方法:

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

在這種情況下,你可以說:

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

拿到星期的名字。在這種情況下,t.date()將返回一個Date對象,該對象依次用於調用dayOfWeek()

+0

+1僅用於第一個短語,-1不用說'Date Time :: date()const;'並且使用冗餘空白。因此,+ -0。編輯:編輯你的答案後,我會+1。 –

+0

@ phresnel,這是一種做法,但與我的意圖不同。爲什麼要返回一個新對象而不是現有對象的引用?返回對現有對象的引用似乎同樣有效,如[在此](http://en.wikipedia.org/wiki/Method_chaining)和[在此](http://www.parashift.com/c++-faq-精簡版/ ctors.html#FAQ-10.20)。當你考慮鏈接經常被用來配置一個新的對象時,它也似乎更有效率和更自然。 – Caleb

+0

好吧,我以爲那些是錯別字。你確定'dayOfWeek()'應該返回一個非const字符串的引用嗎?爲什麼多餘的「空白」?此外,這些示例返回'* this',而不是另一種類型的實例,它可以非常巧妙地引入可能在您的客戶之前隱藏的危險錯誤。 –

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();  

你獲得秒,而無需將語句分成兩條不同的語句或製作一個臨時變量保存返回的時間對象。

這個問題C++: Using '.' operator on expressions and function calls如果你想讀更深入一點。

1

因爲當一個函數被執行並返回時,它會返回對自身的引用,所以它可以再次調用functions