簡體   English   中英

返回值(引用,指針和對象)

[英]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構成,臨時cfoo()被構造,[復制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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM