標籤:

c++primer和more effective c++的一處矛盾?

primer說引用捕獲和函數的一樣,而more effective c++說無論是傳值還是傳引用都會複製,誰對誰錯?


對於這種問題,其實試一下就都明白了。

先看下面這段 Sample Code,為了驗證 throw 之後發生了什麼,我們自定義一個異常類,在它的構造函數中我們計數,每執行一次默認構造函數就讓計數器 +1,如果執行拷貝構造器就將拷貝後的 m_c 左移一位。然後我們在其析構函數中列印出計數器的值和對象的地址。

第一種情況,catch clause 使用 by value 傳值:

可以看到異常類的默認構造函數執行了一次,當然是我們創建 e 的時候調用的。緊接著我們能看到複製構造器被調用了,e 被複制到了 0x1006037C8 (tagged 2) 地址中去了,從地址能看出 e 被複制到了一塊堆內存,編譯器將負責其清理工作,然後由於 catch clause 是 by value,tagged 2 對象又被複制到了 except 變數中,此時拷貝構造器調用,計數器為 4。從析構順序我們可以知道,tagged 1 對象在 throw 語句執行後首先析構,實際上 throw 所在的 scope 中的所有對象都會析構,然後編譯器將 e 複製到堆內存,再複製到 except 變數,catch clause 被執行,執行後 except 首先析構,最後堆內存中的 e 副本才會析構。

然後我們看 by ref 的情況:

顯然,從堆內存複製到 except 變數的過程沒有了,except 就是堆內存副本對象的別名。雖然解釋的不算仔細,但是題主到這裡應該能理解了,事實上這種問題自己試試完全可以解決。


都是對的,你理解錯了。


throw無論如何都會發生拷貝,而catch是否發生拷貝看是「值捕獲」還是「引用捕獲」。more effective c++ 其實是清楚地指出了這點,題主可能沒注意到。

所以用「值捕獲」會發生兩次拷貝,而用「引用捕獲」會發生一次拷貝。

注意上面所說的「throw無論如何都會發生拷貝」是在第一次拋出異常時,如果是「重新拋出」,使用下面的語句,則不會拷貝:

catch(const exception e)

{

throw;

}

但如果是

catch(const exception e)

{

throw e;

}

則又會發生拷貝,並且拋出的是catch里的靜態類型。所以more effective c++有提到,一般使用 「throw;」 ,而不是「throw e;」。同樣的問題,在《c++編程規範:101條規則,準則與最佳實踐》第73條也有提及。


推薦閱讀:

如何判斷CTP的行情線程是否阻塞?
Qt 重繪問題?
如何評價基於 C++ 17 的框架 MCF?
protobuf 變長64位無符號整數 為什麼最多需要消耗10位元組而不是9位元組?
C++ 為什麼 Lambda 的引用捕獲 const 變數會失敗?

TAG:編程 | C | CC | CPrimer |