【C++ Primer 第七章】 隱式的類類型轉換
轉換構造函數
轉換構造函數:如果構造函數只接受一個實參,則實際上定義了轉換此類類型的隱式轉換機制,有時我們把這種構造函數稱作轉換構造函數。
能通過一個實參調用的構造函數定義了一條從構造函數的參數類型向類類型隱式轉換的規則。
例如,在Sales_data類中,接受string的構造函數和接受istream的構造函數分別定義了從這兩種類型向Sales_data隱式轉換的規則。也就是說,在需要使用Sales_data的地方,我們可以使用string或者istream作為替代:
構造函數: Sales_data(const string &s); Sales_data(istream&); Sales_data &combine(const Sales_data&); string null_book="9-999-99999-9"; item.combine(null_book); //構造一個臨時的Sales_data對象 //該對象的units_sold和revenue等於0,bookNo等於null_book
在這裏我們用一個string實參調用了Sales_data的combine成員。該調用時合法的,編譯器用給定的string自動創建一個Sales_data對象。新生成的這個(臨時)Sales_data對象被傳遞給combine。因為combine的參數是一個常量引用,所以我們可以給該參數傳遞以臨時量
只允許一步類型轉換
• 編譯器只會自動地執行一步類型轉換。
例如,因為下面的代碼隱式地使用了兩種轉換規則,所以它是錯誤的:
//錯誤:需要用戶定義的兩種轉換: //(1)把“9-999-99999-9”轉換成string //(2)再把這個(臨時的)string轉換成Sales_data item.combine("9-999-99999-9");
如果我們想完成上述調用,可以顯式地把字符串轉換成string或者Sales_data對象:
//正確:顯式地轉換成string,隱式地轉換成Sales_data item.combine(string("9-999-99999-9")); //正確:隱式地轉換成string,顯式地轉換成Sales_data item.combine(Sales_data("9-999-99999-9"));
類型轉換不是總有效
是否需要從string到Sales_data的轉換依賴於我們對用戶使用該轉換的看法。在此例中,這種轉換可能是對的。null_book中的string可能表示了一個不存在的ISBN編號。
另一個從istream到Sales_data的轉換:
//使用istream構造函數創建一個函數傳遞給combine item.combine(cin);
這段代碼隱式地把cin轉換成Sales_data,這個轉換執行了接受一個istream的Sales_data構造函數。該構造函數通過讀取標準輸入創建了一個(臨時的)Sales_data對象,隨後將得到的對象傳遞給combine。
Sales_data對象是個臨時量,一旦combine完成我們就不能再訪問它了。實際上,我們構建了一個對象,先將它的值加到item中,隨後將其丟棄。
抑制構造函數定義的隱式轉換
在要求隱式轉換的程序上下文中,我們可以通過將構造函數聲明為explicit加以阻止:
1 class Sales_data{ 2 public: 3 Sales_data()=default; 4 Sales_data(const std::string &s,unsigned n,double p): 5 bookNo(s),units_sold(n),revenue(p*n) {} 6 explicit Sales_data(const std::string &s):bookNo(s) {} 7 explicit Sales_data(std::istream&); 8 //其他成員與之前的一致 9 };
此時,沒有任何構造函數能用於隱式地創建Sales_data對象,之前的兩種用法都無法通過編譯:
item.combine(null_book); //錯誤:string構造函數是explicit item.combine(cin); //錯誤:istream構造函數是explicit的
• 關鍵字explicit只對一個實參的構造函數有效。需要多個實參的構造函數不能用於執行隱式轉換,所有無須將這些構造函數指定為explicit的。
• 只能在類內聲明構造函數時使用explicit關鍵字,在類外部定義時不應重復:
//錯誤:explicit關鍵字只允許出現在類內的構造函數聲明處 explicit Sales_data::Sales_data(istream& is) { read(is,*this); }
explicit構造函數只能用於直接初始化
發生隱式轉換的一種情況是當我們執行拷貝形式的初始化時(使用=)。此時,我們只能使用直接初始化而不能使用explicit構造函數:
Sales_data item1(null_book); //正確:直接初始化 Sales_data item2=null_book; //錯誤:不能將explicit構造函數用於拷貝形式的初始化過程
註意:當我們使用explicit關鍵字聲明構造函數時,它將只能以直接初始化的形式使用。而且,編譯器將不會在自動轉換過程中使用該構造函數。
為轉換顯式地使用構造函數
盡管編譯器不會將explicit的構造函數用於隱式轉換過程,但是我們使用這樣的構造函數顯式地強制進行轉換“
//正確:實參是一個顯式構造的Sales_data對象 item.combine(Sales_data(null_book)); //正確:static_cast可以使用explicit的構造函數 item.combine(static_cast<Sales_data>(cin));
在第一調用中,我們直接使用Sales_data的構造函數,該調用通過接受string的構造函數創建了一個臨時的Sales_data對象。第二個調用,我們使用static_cast執行了顯式的而非隱式的轉換。其中,static_cast使用istream構造函數創建了一個臨時的Sales_data對象。
【C++ Primer 第七章】 隱式的類類型轉換