日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【C++】C++函数需要有返回值,但非全分支return(RVO)

發布時間:2024/1/1 c/c++ 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++】C++函数需要有返回值,但非全分支return(RVO) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天在review以前的代碼的時候,遇到了一個比較奇怪的現象,函數的有返回值,但只在if后面有return,else后面忘寫了。但這個版本的代碼已經調試驗證通過了,沒有問題的,這就很怪異。


考驗一道題

下面這道題Print的內容是什么?

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;} };Test fun(bool flag) {Test t (0, "0");if (flag) {t.x = 1;t.y = "1";return t;} else {t.x = -1;t.y = "-1";} }int main(int argc, char const *argv[]) {Test t = fun(false);t.Print();return 0; }

編譯并運行:

g++ test.cpp -o test ./test

可能你也能猜到最終的結果:

x : -1 y : -1

也可能會疑問,在fun函數的else語句中并沒有提供返回值啊?為什么還能有輸出么?或者,會問函數的return語句不全,不是應該報錯么?


函數的返回值

沒有return語句

如果一個函數需要有返回值,但是沒有return語句,這會出現什么?

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;} };Test fun(bool flag) {Test t (0, "0");if (flag) {t.x = 1;t.y = "1";} else {t.x = -1;t.y = "-1";} }int main(int argc, char const *argv[]) {Test t = fun(false);t.Print();return 0; }

編譯并運行:

g++ test.cpp -o test ./test

編譯沒有任何問題,但是在運行的時候出現了錯誤:

x : 2 y : H��H9�u�H�[]A\A]A^A_Df.���H�H��x : y : 01-1;d����h����^���h���������U����j�������� <���Hh��������@zRx�����*zRx�$ *** Error in `./test': munmap_chunk(): invalid pointer: 0x000000000040112d *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f4648daf7e5] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f4648dbc698] ./test[0x4010d0] ./test[0x400f56] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f4648d58830] ./test[0x400d09] ======= Memory map: ======== 00400000-00402000 r-xp 00000000 08:01 263039 /home/yngzmiao/test/test/test 00601000-00602000 r--p 00001000 08:01 263039 /home/yngzmiao/test/test/test 00602000-00603000 rw-p 00002000 08:01 263039 /home/yngzmiao/test/test/test 01656000-01688000 rw-p 00000000 00:00 0 [heap] 7f4648a2f000-7f4648b37000 r-xp 00000000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4648b37000-7f4648d36000 ---p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4648d36000-7f4648d37000 r--p 00107000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4648d37000-7f4648d38000 rw-p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4648d38000-7f4648ef8000 r-xp 00000000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f4648ef8000-7f46490f8000 ---p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f46490f8000-7f46490fc000 r--p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f46490fc000-7f46490fe000 rw-p 001c4000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f46490fe000-7f4649102000 rw-p 00000000 00:00 0 7f4649102000-7f4649118000 r-xp 00000000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4649118000-7f4649317000 ---p 00016000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4649317000-7f4649318000 rw-p 00015000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4649318000-7f464948a000 r-xp 00000000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f464948a000-7f464968a000 ---p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f464968a000-7f4649694000 r--p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4649694000-7f4649696000 rw-p 0017c000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4649696000-7f464969a000 rw-p 00000000 00:00 0 7f464969a000-7f46496c0000 r-xp 00000000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f464989d000-7f46498a3000 rw-p 00000000 00:00 0 7f46498be000-7f46498bf000 rw-p 00000000 00:00 0 7f46498bf000-7f46498c0000 r--p 00025000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f46498c0000-7f46498c1000 rw-p 00026000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f46498c1000-7f46498c2000 rw-p 00000000 00:00 0 7ffc906a9000-7ffc906ca000 rw-p 00000000 00:00 0 [stack] 7ffc90796000-7ffc90799000 r--p 00000000 00:00 0 [vvar] 7ffc90799000-7ffc9079b000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 已放棄 (核心已轉儲)

也就是說,如果一個函數聲明或定義了有返回值,那么就必須要有return語句

那么,如果出現了分支語句,必須保證所有的出口都必須有return么?也就是本文開始提出的那個問題。當然,答案也已經被證明出來了,并不需要。

非全出口return語句

在正式解釋之前,再看一個例子:

# include <iostream>class Test {public:Test (int xx, std::string yy) {x = xx;y = yy;}int x;std::string y;void Print() {std::cout << "x : " << x << " y : " << y << std::endl;} };Test fun(bool flag) {if (flag) {Test t (0, "0");t.x = 1;t.y = "1";return t;} else {Test t (0, "0");t.x = -1;t.y = "-1";} }int main(int argc, char const *argv[]) {Test t = fun(false);t.Print();return 0; }

編譯并運行:

g++ test.cpp -o test ./test

編譯沒有任何問題,但是在運行的時候出現了錯誤:

x : 2 y : H��H9�u�H�[]A\A]A^A_Df.���H�H��x : y : 01-1;l����h����^����^���������"���8�������( ���P*���ph��� ����hzRx�����*zRx*** Error in `./test': munmap_chunk(): invalid pointer: 0x00000000004012ad *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f4ad9fd07e5] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f4ad9fdd698] ./test[0x40121e] ./test[0x4010a3] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f4ad9f79830] ./test[0x400d89] ======= Memory map: ======== 00400000-00402000 r-xp 00000000 08:01 262629 /home/yngzmiao/test/test/test 00601000-00602000 r--p 00001000 08:01 262629 /home/yngzmiao/test/test/test 00602000-00603000 rw-p 00002000 08:01 262629 /home/yngzmiao/test/test/test 011d1000-01203000 rw-p 00000000 00:00 0 [heap] 7f4ad9c50000-7f4ad9d58000 r-xp 00000000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4ad9d58000-7f4ad9f57000 ---p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4ad9f57000-7f4ad9f58000 r--p 00107000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4ad9f58000-7f4ad9f59000 rw-p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7f4ad9f59000-7f4ada119000 r-xp 00000000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f4ada119000-7f4ada319000 ---p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f4ada319000-7f4ada31d000 r--p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f4ada31d000-7f4ada31f000 rw-p 001c4000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7f4ada31f000-7f4ada323000 rw-p 00000000 00:00 0 7f4ada323000-7f4ada339000 r-xp 00000000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4ada339000-7f4ada538000 ---p 00016000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4ada538000-7f4ada539000 rw-p 00015000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f4ada539000-7f4ada6ab000 r-xp 00000000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4ada6ab000-7f4ada8ab000 ---p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4ada8ab000-7f4ada8b5000 r--p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4ada8b5000-7f4ada8b7000 rw-p 0017c000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7f4ada8b7000-7f4ada8bb000 rw-p 00000000 00:00 0 7f4ada8bb000-7f4ada8e1000 r-xp 00000000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f4adaabe000-7f4adaac4000 rw-p 00000000 00:00 0 7f4adaadf000-7f4adaae0000 rw-p 00000000 00:00 0 7f4adaae0000-7f4adaae1000 r--p 00025000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f4adaae1000-7f4adaae2000 rw-p 00026000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7f4adaae2000-7f4adaae3000 rw-p 00000000 00:00 0 7ffeaa7a8000-7ffeaa7c9000 rw-p 00000000 00:00 0 [stack] 7ffeaa7fa000-7ffeaa7fd000 r--p 00000000 00:00 0 [vvar] 7ffeaa7fd000-7ffeaa7ff000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 已放棄 (核心已轉儲)

和第二例沒有return的語句一樣,也出現了類似的錯誤。但這段程序是有return語句的啊!

細細分析代碼的區別,其實可以發現,分支中沒有return分支的函數出口,好像有一個默認的返回值,就是有return語句分支的返回值變量的所在地址

第一例中,else語句盡管沒有return語句,但是返回了一個與if語句中return的同一個變量地址的內容;而第三例中,else語句中并沒有if語句中return的同一變量地址的內容(棧被釋放了)!

當然,這些都是通過區分代碼得到的淺顯的認知,但這究竟是什么原因呢?

編譯器的鍋(RVO)

一般來說,為了從一個函數得到運行結果,常規的途徑有兩個:通過返回值和通過傳入函數的引用或指針

當通過返回值的時候,如果是類的對象或指針的時候,需要注意拷貝構造函數

拷貝構造函數通常有三種使用場景:

  • 用一個對象初始化另一個對象;
  • 函數的形參與實參結合時;
  • 函數返回時,棧上對象復制到返回值中時。
  • 也就是說,當函數返回時,需要將棧上的對象通過拷貝構造函數,復制到調用該函數的返回值中去。即,把return t的t復制到Test t = fun(false)的t中去。

    而RVO(C++的返回值優化)是指:C++標準允許一種(編譯器)實現省略創建一個只是為了初始化另一個同類型對象的臨時對象。基本手段是直接將返回的對象構造在調用者棧幀上,這樣調用者就可以直接訪問這個對象而不必復制。指定這個參數(-fno-elide-constructors)將關閉這種優化,強制G++在所有情況下調用拷貝構造函數。

    現在就清楚了,當函數有一個return后,就會將該return的對象直接構造在調用者棧上,就不需要走return的拷貝構造函數。

    當然可以試驗一下,利用-fno-elide-constructors關閉優化:

    編譯并運行:

    g++ -fno-elide-constructors test.cpp -o test ./test

    編譯沒有任何問題,但是在運行的時候出現了錯誤:

    *** Error in `./test': munmap_chunk(): invalid pointer: 0x00000000004012bd *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fdcb3df47e5] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7fdcb3e01698] ./test[0x401228] ./test[0x40105d] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fdcb3d9d830] ./test[0x400d89] ======= Memory map: ======== 00400000-00402000 r-xp 00000000 08:01 262629 /home/yngzmiao/test/test/test 00601000-00602000 r--p 00001000 08:01 262629 /home/yngzmiao/test/test/test 00602000-00603000 rw-p 00002000 08:01 262629 /home/yngzmiao/test/test/test 00f88000-00fba000 rw-p 00000000 00:00 0 [heap] 7fdcb3a74000-7fdcb3b7c000 r-xp 00000000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7fdcb3b7c000-7fdcb3d7b000 ---p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7fdcb3d7b000-7fdcb3d7c000 r--p 00107000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7fdcb3d7c000-7fdcb3d7d000 rw-p 00108000 08:01 5247874 /lib/x86_64-linux-gnu/libm-2.23.so 7fdcb3d7d000-7fdcb3f3d000 r-xp 00000000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7fdcb3f3d000-7fdcb413d000 ---p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7fdcb413d000-7fdcb4141000 r--p 001c0000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7fdcb4141000-7fdcb4143000 rw-p 001c4000 08:01 5247804 /lib/x86_64-linux-gnu/libc-2.23.so 7fdcb4143000-7fdcb4147000 rw-p 00000000 00:00 0 7fdcb4147000-7fdcb415d000 r-xp 00000000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fdcb415d000-7fdcb435c000 ---p 00016000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fdcb435c000-7fdcb435d000 rw-p 00015000 08:01 5247842 /lib/x86_64-linux-gnu/libgcc_s.so.1 7fdcb435d000-7fdcb44cf000 r-xp 00000000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7fdcb44cf000-7fdcb46cf000 ---p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7fdcb46cf000-7fdcb46d9000 r--p 00172000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7fdcb46d9000-7fdcb46db000 rw-p 0017c000 08:01 2099340 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21 7fdcb46db000-7fdcb46df000 rw-p 00000000 00:00 0 7fdcb46df000-7fdcb4705000 r-xp 00000000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7fdcb48e2000-7fdcb48e8000 rw-p 00000000 00:00 0 7fdcb4903000-7fdcb4904000 rw-p 00000000 00:00 0 7fdcb4904000-7fdcb4905000 r--p 00025000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7fdcb4905000-7fdcb4906000 rw-p 00026000 08:01 5247776 /lib/x86_64-linux-gnu/ld-2.23.so 7fdcb4906000-7fdcb4907000 rw-p 00000000 00:00 0 7ffe40205000-7ffe40226000 rw-p 00000000 00:00 0 [stack] 7ffe40320000-7ffe40323000 r--p 00000000 00:00 0 [vvar] 7ffe40323000-7ffe40325000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 已放棄 (核心已轉儲)

    也就是說,確實是因為RVO(C++的返回值優化)的原因。

    當然,保證每個分支出口都有return才是最重要的

    相關閱讀

    【C++踩坑】說說g++的-fno-elide-constructors參數
    C++的返回值優化(RVO,Return Value Optimization)

    總結

    以上是生活随笔為你收集整理的【C++】C++函数需要有返回值,但非全分支return(RVO)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。