2011-09-11 39 views
3

當main()調用具有某種數據類型(基本或用戶定義的)的返回值的函數時,調用該函數的語句通常是一個賦值。帶返回值的函數(C++)

例如: -

class complex 
{ 

private: 

    int real; 
    int imag; 

public: 

    complex(); 
    complex(int r,int i); 
    complex operator + (complex c); 
}; 

想,我的定義重載 「+」 是這樣的 -

complex complex::operator + (complex c) 
{ 
    this->real = this->real + c.real; 
    this->imag = this->imag + c.imag; 
    return *this; 
} 

我有我的主要功能如下 -

int main() 
{ 
    complex c1; 
    complex c2(5,4); 
    c1+c2; 
} 

在上面的main()中,考慮語句c1 + c2。編譯器將其視爲c1.operator +(c2)。當這個函數被main調用時,它將一個值返回給main()。這個返回值會發生什麼?

+0

不要重載'運算符+'那樣。你能想象如果說'y = x + 7'修改了'x'的值嗎? –

+0

@Travis ..感謝您的建議:) ..我明白了。但我是C++的初學者,並試圖以各種方式探索事物。 – jsp99

+0

挑剔。不要通過按值傳遞rhs來重載+運算符。通過參考傳遞。那就是將函數聲明爲「complex&complex :: operator +(complex&c)」。 – selbie

回答

2

它被分配到一個時間(如果你可能會看到)複雜的價值。這種價值的生命週期直到生成它的表達結束,那就是;在c1 + c2的末尾。因此,創建一個新的時間對象來存儲表達式的結果,並在該行結尾處將其破壞。

在評估a + b之後,您應該不會在您的a + b運算符中修改「this」,並保留表達式的結果。

+0

但是生成_temporal object_的表達式是** return * this ** ..難道是這樣嗎? – jsp99

+0

不,返回*這會產生另一個時間,然後被複制到從函數返回的時間(編譯器被允許刪除該副本,他們通常會這樣做)。該臨時對象是通過複製構造函數的返回值而生成的。 –

+0

我不知道這是對還是錯。但這就是我所理解的 - _「語句** c1.operator +(c2); **,說'鬆散地'有一個臨時對象(讓我們說t1)和語句** return * this; **在執行時創建一個(比如說t2)。當返回語句的**; **達到時** t2被銷燬**並且它的值已經存儲在** t1 **中。現在,在這個特定的場景中,**執行達到**; **的陳述** c1.operator +(c2); **「_ – jsp99

1

在這種情況下,它會自動丟棄(但總和存儲在c1c2)。編譯器可能(或可能不)通過完全刪除該行來優化代碼,因爲它沒有做任何實質性的工作。由此產生的金額將由operator+(將創建一個臨時變量)構造並返回,然後立即銷燬。

這也發生在其他情況下。試想一下:

c2 += c1; 

你可以連續數增加像這樣一起:

c4 += c3 += c2 += c1; 

這是因爲operator+=返回一個值,但它是在你的代碼忽略等。我想你想用operator+=

0

它只是被丟棄,但AFAIK C++會顯示錯誤,當函數期望返回值沒有返回語句。

2

return值被丟棄;因爲你不在任何地方存儲它。

一個重要的建議

理想的operator +的定義應該是這樣。

complex complex::operator + (const complex& c) 
{       ^^^^^^^^^^^^^^ 
    complex add; 
    add.real = this->real + c.real; 
    add.imag = this->imag + c.imag; 
    return add; 
} 

在您的原始代碼中,製作了兩份complex;其中至少有1個可以用上述格式避免,參數爲,通過const引用傳遞。

另外,在你的代碼中你不應該改變當前對象; (否則就像operator +=())。因此,在函數內部創建一個臨時文件並按值傳遞它。

+2

+1的建議,銷燬t1 **。但是,這些建議並不完全和語義上正確。 'operator +'不應該改變其被調用的對象。也就是說,'a + b'不應該改變'a',而應該'運算符+'應該創建一個局部變量,並且應該返回。所以簽名會是'complex complex :: operator +(const complex&c)const'。使函數成爲常量。 – Nawaz

+0

我的意思是,*就是說,'a + b'不應該改變'a' *。我編輯了我以前的評論。 – Nawaz

+0

@Nawaz,正確。實際上,由於在實際代碼中沒有使用'a + b'的結果,我想也優化了「返回」值。 ;)然而,這不是一個好的建議,所以編輯它。 – iammilind

8

您的代碼會忽略表達式c1+c2的值,因爲您沒有將它存儲在任何位置。編譯器至多會打印一些警告消息。爲了剿這樣的警告信息,你可以寫:

(void)(c1+c2); //casting to void suppresses the warning messages! 

看到這個:


與您的代碼真正的問題..

然而,在你的代碼中,operator+的實現在語義上不是c orrect。據瞭解,考慮這個,

int a=10; 
int b=5; 

那麼,你希望a+b改變a價值? a應該變成15?號碼

如果你想要的話,那麼你會寫a+=b來代替。但在您的代碼中,c1+c2的行爲等同於c1+=c2的語義,因爲您在執行operator+時更新了this->realthis->imag的值,這在語義上是不正確的。

所以第一個解決方法是這樣的:

complex complex::operator + (const complex& c) const 
{        
    complex result(c); //make a copy of c 
    result.real += this->real; //add 
    result.imag += this->imag; //add 
    return result; 
} 

現在,這是語義正確的。

這就是說,還有幾件事值得注意。當你寫c1+c2時,你認爲操作+是應用於任何一個對象嗎?不。它不適用於它們中的任何一個,但成員函數operator+c1對象上被調用,該對象在函數內變成this指針。爲什麼要在c1(或者對於c2)調用它,如果該操作不適用於它?

此分析表明operator+不應該是該類的成員函數。這應該是一個非成員函數代替,然後簽名是:

complex operator+(const complex &a, const complex &b); 

但有一個小問題:在a+b計算,它需要訪問類的私有成員(realimag是私人成員)。因此該解決方案是,operator+應在operator+=方面來實現,並且後者應該被添加爲成員函數到類,因爲在表達式a+=b操作+=不上a適用,因爲它修改它的值。

因此,這裏是我的實現無論是運營商的:

class complex 
{ 
    //... 
    public: 

    //member function 
    complex& operator+=(const complex & c) 
    { 
      real += c.real; //same as: this->real+=c.real; this is implicit 
      imag += c.imag; //same as: this->imag+=c.real; this is implicit 
      return *this; 
    } 
}; 

//non-member function (its non-friend as well) 
complex operator+(const complex &a, const complex &b) 
{ 
    complex result(a); //make copy of a by invoking the copy-constructor 
    result += b; //invokes operator+ 
    return result; 
} 

或者你可以參加最後兩個語句:

complex operator+(const complex &a, const complex &b) 
{ 
    complex result(a); //make copy of a by invoking the copy-constructor 
    return result += b; //invokes operator+, and then return updated 'result' 
} 

但還有另一種方法,使副本。爲什麼通過引用傳遞兩個參數?按值傳遞第一個參數將會生成我們需要的副本。所以更好的實施將是這樣的:

complex operator+(complex a, const complex &b) 
{    //^^^^^^^^^ pass-by-value 
    a += b; //a is a copy, after all - we can modify it! 
    return a; 
} 

希望有所幫助。

+1

在線演示:http://ideone.com/KHc3w – Nawaz

+0

感謝戰利品:)。我知道用我給出的定義來使用operator +是不合適的。但我並沒有強調這一事實。我的錯誤是「鬆散地」寫了這個函數,但是我真正想知道的是K-ballo和我正在討論的內容。請你可以留下你的意見嗎? – jsp99

+0

很好的答案,但我不確定我是否相信你的關於operator +()屬於課外的觀點。如果我們按照你的推理,一個類的每個const成員函數都應該成爲一個非成員函數,接受一個對象的const引用,因爲它不會「應用」它。我認爲它是一個成員是有意義的,因爲它在概念上與班級相關。 – neuviemeporte

0

另一種解決方案是使用operator +在全球範圍內,但爲你的類的朋友:

class complex 
{ 
    // ... 

public: 

    // ... 

    friend complex operator +(const complex &c1, const complex &c2) 
    { 
     complex c; 
     c.real = c1.real + c2.real; 
     c.imag = c1.imag + c2.imag; 
     return c; 
    } 

    // ... 

}; 

使用這種技術,你也可以通過添加使用一個整數作爲參數(在左):

friend complex operator +(const int n, const complex &c) 
    { 
     complex c; 
     c.real = n + c.real; 
     c.imag = c.imag; 
     return c; 
    } 

    friend complex operator +(const complex &c, const int n) 
    { 
     complex c; 
     c.real = c.real + n; 
     c.imag = c.imag; 
     return c; 
    } 

所以,現在你也可以做

complex c1, c2, c3; 
c3 = 1 + c2 + 8 + c1 + 2; 

和你的類缺少(VIR tual)析構函數,我也會使成員變量受保護而不是私人:)