2013-09-28 98 views
2

我在理解C++中返回值背後真正做了些什麼困難。返回值(引用,指針和對象)

讓我們下面的代碼:

class MyClass { 

public: 

    int id; 

    MyClass(int id) { 
     this->id = id; 
     cout << "[" << id << "] MyClass::ctor\n"; 
    } 

    MyClass(const MyClass& other) { 
     cout << "[" << id << "] MyClass::ctor&\n"; 
    } 

    ~MyClass() { 
     cout << "[" << id << "] MyClass::dtor\n"; 
    } 

    MyClass& operator=(const MyClass& r) { 
     cout << "[" << id << "] MyClass::operator=\n"; 
     return *this; 
    } 

}; 

MyClass foo() { 
    MyClass c(111); 
    return c;   
} 

MyClass& bar() { 
    MyClass c(222); 
    return c; 
} 

MyClass* baz() { 
    MyClass* c = new MyClass(333); 
    return c; 
} 

我使用gcc 4.7.3。

案例1

當我打電話:

MyClass c1 = foo(); 
cout << c1.id << endl; 

輸出是:

[111] MyClass::ctor 
111 
[111] MyClass::dtor 

我的理解是,在foo對象是在棧上創建,然後在銷燬返回語句,因爲它是範圍的結尾。返回是通過對象複製(複製構造函數)完成的,稍後將其分配給主(賦值運算符)中的c1。如果我是正確的,爲什麼沒有複製構造函數或賦值運算符的輸出?這是因爲RVO嗎?

案例2

當我打電話:

MyClass c2 = bar(); 
cout << c2.id << endl; 

輸出是:

[222] MyClass::ctor 
[222] MyClass::dtor 
[4197488] MyClass::ctor& 
4197488 
[4197488] MyClass::dtor 

這到底是怎麼回事?我創建變量然後返回它,變​​量被銷燬,因爲它是一個範圍的結束。編譯器試圖通過拷貝構造函數拷貝該變量,但它已經被銷燬,這就是爲什麼我有隨機值?那麼主要在c2究竟是什麼?

案例3

當我打電話:

MyClass* c3 = baz(); 
cout << c3->id << endl; 

輸出是:

[333] MyClass::ctor 
333 

這是最簡單的情況?我返回一個動態創建的指針,位於堆上,所以memmory被分配,而不是自動釋放。當析構函數沒有被調用並且我有內存泄漏時,就是這種情況。我對嗎?

是否還有其他不明顯的情況或事情,我應該知道要充分掌握C++中的返回值? ;)從函數返回一個對象的建議方法是什麼(如果有的話) - 對此的任何經驗規則?

+1

'MyClass c1 = foo();'不是一個賦值。這是一個*複製初始化*。 – dyp

+0

嗯,我的錯。所以在所有這些情況下'operator ='不會被調用? – jesper

+2

確實。經驗法則:除非你想暴露內部(像'std :: vector :: operator []'返回一個引用),否則不要返回擁有的原始指針(即一個必須被刪除的),不要返回一個const對象(因爲它禁止了移動語義)。 – dyp

回答

2

案例1

MyClass foo() { 
    MyClass c(111); 
    return c;   
} 
... 
MyClass c1 = foo(); 

是當可以應用RVO一個典型的例子。這就是所謂的複製初始化和賦值運算符不使用,因爲對象是在地方建立,不同的情況:

MyClass c1; 
c1 = foo(); 

其中c1構造,在foo()臨時c構造,[中c副本cc的副本被分配給c1,[c的副本被銷燬]並且c被銷燬。 (究竟發生什麼取決於編譯器是否消除了正在創建的c的冗餘副本)。

案例2

MyClass& bar() { 
    MyClass c(222); 
    return c; 
} 
... 
MyClass c2 = bar(); 

調用未定義行爲因爲你正在返回本地(臨時)可變c參考〜具有自動存儲持續時間的對象。

案例3

MyClass* baz() { 
    MyClass* c = new MyClass(333); 
    return c; 
} 
... 
MyClass c2 = bar(); 

是因爲你控制會發生什麼尚未有一個非常不愉快的後果最直接的一個:你負責內存管理,這是什麼原因爲什麼總是應該避免這種動態分配(並且更喜歡案例1)。

0

1)是的。
2)您有一個隨機值,因爲您的副本c'tor和operator=不會複製id的值。但是,假定在刪除對象後不依賴對象的值,則是正確的。
3)是的。

3

我可以補充一點:case#2是C++語言中未定義行爲的一種情況,因爲返回對本地變量的引用是非法的。這是因爲局部變量具有精確定義的生命週期,並且 - 通過返回引用 - 您將返回對函數返回時不再存在的變量的引用。因此,您展現未定義的行爲,並且給定變量的值實際上是隨機的。 As is the result of the rest of your program, since Anything at all can happen。當您嘗試做這樣的事情

大多數編譯器會發出警告(通過引用,或通過地址返回一個局部變量) - 海灣合作委員會,例如,告訴我這樣的事情:

bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr] 

但是,您應該記住,當可能會出現未定義行爲的語句發生時,編譯器根本不需要發出任何警告。但是,像這樣的情況必須不惜一切代價來避免,因爲它們實際上從來都不對。