作者:負一的平方根
連結:https://sqrt-1.me/?p=241
C++中的直接初始化指的是直接調用類的構造函數進行初始化,例如:
複製初始化指的是用「=」號來初始化對象,例如:
在上面的例子中,這兩種寫法是完全等效的,但是直接初始化和複製初始化在一些情況下還是有區別的。
根據C++的標準,直接初始化就是直接調用類的構造函數來初始化對象,例如在string a(「hello」)中,string類的string(const char *)構造函數會被調用,a被直接初始化。
然而根據標準,複製初始化應該是先調用對應的構造函數創建一個臨時對象,再調用拷貝構造函數將臨時對象拷貝給要創建的對象。例如在string a=」hello」中,string類的string(const char *)構造函數會被首先調用,創建一個臨時對象,然後拷貝構造函數將這個臨時對象複製到a。
標準還規定,為了提高效率,允許編譯器跳過創建臨時對象這一步,直接調用構造函數構造要創建的對象,這樣就完全等價於直接初始化了。
經過我的測試,常見的編譯器(gcc和VC++)都會直接跳過創建臨時對象這一步,即使沒有打開任何優化選項。
這樣一來,也許你就會認為他們完全等同了,其實不然。下面分析兩種情況。
1、當拷貝構造函數為private時。
雖然編譯器會跳過創建臨時對象這一步,但是這個類必須要能夠正確的調用指定的構造函數和拷貝構造函數才能編譯通過。優化是編譯器採取的措施,但是程序必須使得不優化時代碼也符合語法。
C++中有一些系統類型不允許複製,例如iostream和fstream這樣的類。如果你想把自定義的類設置為不允許複製,把拷貝構造函數和賦值運算符聲明為private即可,前面所說的系統類型也是這樣做的。
我們用ifstream舉例說明直接初始化和複製初始化的區別。用一個字符串來構造ifstream對象,即打開以字符串為文件名的文件輸入流。
我們來分析一下為什麼第二個語句會編譯出錯。根據C++標準,第二個語句應該做以下兩件事情:用」filename」初始化一個臨時的ifstream對象,把臨時的對象用拷貝構造函數複製給file。由於拷貝構造函數是private,所以沒有權限調用此函數,編譯出錯。
註:Visual Studio的C++編譯器並沒有遵循標準,而是不考慮拷貝構造函數是否為private,直接編譯通過。
2、當拷貝構造函數為explicit,或者指定的構造函數為explicit時。
C++中如果一個構造函數為explicit,那麼只能顯式調用這個構造函數,把這個構造函數用作「隱式類型轉換」是不可以的。
我們定義一個類A,然後進行以下測試
可以編譯通過。然後我們把參數為int的構造函數改為explicit,代碼如下
「A a(1)」可以編譯通過,「A b=1」這句報錯,錯誤信息是「conversion from 『int』 to non-scalar type 『A』 requested」。這意味著編譯器找不到int類型轉換成A類型的轉換函數了。如果不加explicit,參數為int的構造函數可以用於自動把int轉換為A類型,但是現在不可以了。第一句的「A a(1)」有參數為int的構造函數的顯式調用,而第二句「A b=1」沒有顯式調用。
下面我們只把拷貝構造函數改成explicit
還是第二句編譯錯誤。錯誤信息是「no matching function for call to 『A::A(A)』」,這說明編譯器找不到拷貝構造函數了。和前面第一種情況中說明的一樣,編譯器在把臨時對象複製到b中時,需要調用A類的拷貝構造函數。現在拷貝構造函數是explicit的,只能顯式的調用,不能隱含調用。第二句沒有「A b(temp)」這樣顯式複製對象,所以編譯出錯。
推薦閱讀:
精心整理 | 2017下半年文章目錄
薦號 | 幾個用心的技術公眾號
你所不知道的TIME_WAIT(下)
神經網絡淺講:從神經元到深度學習(二)
專注伺服器後臺技術棧知識總結分享
歡迎關注交流共同進步
碼農有道,為您提供通俗易懂的技術文章,讓技術變的更簡單!