[英]Returning value (reference, pointer and object)
我很難理解在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
對象中會在堆棧上創建該對象,然后在return語句中將其銷毀,因為它是作用域的末尾。 返回是通過對象復制(復制構造函數)完成的,該對象隨后在main(賦值運算符)中分配給c1
。 如果我是對的,為什么復制構造函數和賦值運算符都沒有輸出? 這是因為RVO嗎?
情況二
當我打電話時:
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
這是最簡單的情況嗎? 我返回一個動態創建的指針,該指針位於堆上,因此內存被分配並且不會自動釋放。 當沒有調用析構函數並且內存泄漏時就是這種情況。 我對嗎?
還有其他不明顯的情況或事情,我應該知道完全掌握C ++中的返回值嗎? ;)從功能(如果有的話)中返回對象的推薦方法是什么-關於此的任何經驗法則?
我可以補充一下,情況2是C ++語言中未定義行為的情況之一,因為返回對局部變量的引用是非法的。 這是因為局部變量具有精確定義的生存期,並且-通過引用返回它-您將返回對變量的引用,該變量在函數返回時不再存在。 因此,您表現出未定義的行為,並且給定變量的值實際上是隨機的。 這是程序其余部分的結果,因為一切都可能發生 。
當您嘗試執行以下操作(通過引用或通過地址返回局部變量)時,大多數編譯器都會發出警告-例如,gcc告訴我以下內容:
bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr]
但是,您應該記住,當發生可能表現出未定義行為的語句時,根本不需要編譯器發出任何類型的警告。 但是,必須不惜一切代價避免這種情況,因為它們實際上永遠是不對的。
情況1 :
MyClass foo() {
MyClass c(111);
return c;
}
...
MyClass c1 = foo();
是可以應用RVO的典型情況。 這稱為復制初始化 ,不使用賦值運算符,因為對象是就地創建的,與情況不同:
MyClass c1;
c1 = foo();
其中c1
構成,臨時c
在foo()
被構造,[復制c
構造], c
或副本c
被分配給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)。
1)是的。
2)您有一個隨機值,因為您的副本c'tor和operator=
不會復制id
的值。 但是,您可以假設刪除對象后不依賴任何對象的值是正確的。
3)是的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.