複雜的C ,當函式返回物件到底發生了什麼

2021-10-21 04:07:02 字數 2508 閱讀 2519

我們知道,當函式執行結束的時候,函式內部的區域性變數就會消失,這c/c++裡沒有任何疑問的規定,但是今天我在寫**的時候突然就想到了乙個相當糾結的問題,那就是當我乙個函式返回型別是乙個物件的時候,以我當時掌握的知識理解,當函式返回時回要生成乙個臨時物件,這個臨時物件可能會開銷很多資源,那麼這樣我們的函式就不能設計成乙個返回型別為物件的函式了,或者想辦法避免產生這個臨時物件,方法就是使用動態分配記憶體。出於驗證的想法,我寫了下面一段**進行測試:

#include

using

namespace std;

struct test

test

(const test &)~

test()

};test fun()

intmain()

對於上面的**,我的思考是這樣的:

首先呼叫函式fun(),呼叫建構函式產生t1,輸出t1的位址,到達return t1語句,這時呼叫拷貝建構函式產生乙個臨時物件,t1析構,臨時物件析構,輸出」this is a test!」,程式結束。

沒錯吧?對於上面的思考過程,如果你以前對c++有深入的了解,對這樣的思考過程應該是認同的吧

好,我們用g++編譯執行一下,我的g++版本是gcc version 4.8.4 (ubuntu 4.8.4-2ubuntu1~14.04.3),下面是執行結果

constructor

&t1 is 0x7ffce5a4956f

destroy

this is a test!

不對吧?從這個結果看,好像沒有產生臨時物件啊,難道是我錯了?還是說g++發現我沒有使用函式的返回值然後幫我優化了?ok,我就修改一下**:

int

main()

如果的想法是沒有錯的,那麼過程應該是這樣的:

– 首先呼叫函式fun(),呼叫建構函式產生t1,輸出t1的位址,到達return t1語句,這時呼叫拷貝建構函式產生乙個臨時物件,t1析構,呼叫拷貝建構函式產生物件t2,臨時物件析構,輸出t2的位址,輸出」this is a test!」,t2析構,程式結束。

實際用g++編譯執行的結果是:

constructor

&t1 is 0x7fff5303673f

&t2 is 0x7fff5303673f

this is a test!

destroy

媽呀!這是什麼情況?我怎麼有點懵逼啊?從這個結果看,只在fun()裡呼叫了一次建構函式,產生t1物件,沒有產生臨時物件就算了,把我的t2都搞沒了是怎麼回事?不對,發現乙個新問題,t1物件的析構函式怎麼變成了在程式結束的時候才呼叫?不應該是函式fun()結束後就呼叫的嗎?難道又是編譯器給優化了?我把優化選項關了,編譯時採用的是-o0引數,不過結果還是沒有呼叫拷貝建構函式產生t2,這裡我對優化選項了解得不深,所以我採用了**進行另外的測試。

帶著新問題,我又繼續寫了幾段測試**,測試為什麼沒有產生t1物件,發現真的是編譯器進行了優化(可見-o0選項其實也是對效能進行了一定優化的),我們看看下面這段,編譯器不可以採用優化的:

test fun2

(test const

&t)int

main()

執行的結果是:

constructor

&t3 is 0x7ffec045a2be

copy constructor

&t2 is 0x7ffec045a2bf

this is a test!

destroy

destroy

沒錯,這裡呼叫拷貝建構函式產生了t2了,是編譯器優化了之前的那段**,把原本應該在fun()結束時就析構的t1繼續當成了t2使用,提高了效率。

原來就是g++編譯器幫我們優化,以提高效能的,我們可以通過在編譯的時候,加上-fno-elide-constructors這個選項,來去除這個優化(再次證明我對g++優化沒有乙個系統的學習)

我們重新編譯最開始的那段程式

g++ -o test test.cpp -fno-elide-constructors
執行:

./test
執行結果:

constructor

&t1 is 0x7ffeaf3d693f

copy constructor

destroy

destroy

this is a test!

這下沒錯了,完全符合一開始的猜想,不過這種產生臨時物件的做法效率實在會大大降低,所以g++優化得好啊,不過我們還是要知道編譯器幫我們幹了這麼一回事,以防有什麼特殊情況。

寫到這裡,我又想說了,可能有的人認為不用如此糾結這種問題,但我覺得如果想用真正精通c++,那麼我們就必須知道編譯器糾結都幹了什麼,c++之所以那麼複雜,那麼難,就是因為編譯器會幫我們幹一些我們可能不知道的事情。

函式物件和函式物件當返回值和引數

include using namespace std include string include include include set include include functional 函式物件 類過載了函式呼叫操作符,這樣的類定義的物件稱為函式物件 template class show...

C 函式返回物件為引用

當返回型別為引用時,我們可以用引用來接受,也可以用普通變數來接受。返回型別為普通型別時,例如int,被調函式結束的時候,會生成乙個臨時物件,然後return的內容拷貝給這個臨時物件。但返回型別為引用時,不會生成臨時物件。會直接返回物件。id 正確 id 錯誤,但是不會報錯 sw.setscore s...

c 返回函式區域性物件的引用

在上面的 中,最後能夠輸出正確的值,然而在函式getnode 中,str是乙個區域性的物件,記憶體空間在棧上,當函式退出時,str的記憶體空間被 這是在高階語言的層面上講的。但是為什麼最後的結果是正確的?原因就是node newnode getnode 這句呼叫的是預設的拷貝建構函式,如果是自己重新...