類5(轉換構造函數)
轉換構造函數:
當一個構造函數只有一個參數,而且該參數又不是本類的const引用時,這種構造函數稱為轉換構造函數。
轉換構造函數的作用是將一個其他類型的數據轉換成一個類的對象。註意:轉換構造函數只能有一個參數。如果有多個參數,就不是轉換構造函數:
1 #include <iostream> 2 using namespace std; 3 4 class Sales_data{ 5 6 //數據成員 7 private: 8 std::string book_no; 9 unsigned units_sold = 1; 10 doublerevenue = 1.0; 11 12 public: 13 Sales_data() = default;//不接受任何實參,默認構造函數 14 Sales_data(const std::string &s): book_no(s){}//只有一個形參,類型轉換構造函數 15 Sales_data(const std::string &s, unsigned n, double p): book_no(s), units_sold(n), revenue(p * n){} 16 Sales_data(std::istream&); 17 18Sales_data operator+=(const Sales_data &it){ 19 book_no += it.book_no; 20 units_sold += it.units_sold; 21 revenue += it.revenue; 22 return *this; 23 } 24 25 void out_put(std::ostream &os){ 26 os << book_no << " " << units_sold << "" << revenue << std::endl; 27 } 28 29 Sales_data& combine(const Sales_data&); 30 }; 31 32 Sales_data& Sales_data::combine(const Sales_data &rhs){ 33 book_no += rhs.book_no; 34 units_sold += rhs.units_sold; 35 revenue += rhs.revenue; 36 return *this; 37 } 38 39 // Sales_data operator+(const Sales_data &it1, const Sales_data &it2); 40 41 int main(void){ 42 Sales_data("jfl");//構造一個臨時Sales_data對象 43 Sales_data cnt("hello"); 44 string null_book = " world"; 45 cnt.combine(null_book);//null_book被自動轉換成Sales_data對象並綁定到引用變量rhs上, 46 // 構造出的臨時Sales_data對象中book_no值為null_book,units為1,revenue為1.0 47 // cnt.combine(" world");//需要進行兩步類型轉換,error(" world"要先轉換為string類型,然後再轉換成Sales_data類型) 48 cnt.out_put(cout);//輸出hello world 2 2 49 cnt += null_book;//同上,null_book被自動轉換成Sales_data對象 50 cnt.out_put(cout);//輸出hello world world 3 3 51 return 0; 52 }
還需要註意的是:編譯器只會自動地執行以步類型轉換。所以在上例中 cnt.combine("world");和 cnt += "world");都是錯誤的,因為其隱式的使用了兩種轉換規則,先把 "world" 轉換成 string,再把這個臨時 string 變量轉換成 Sales_data。要使上面調用變成正確的,我們可以先將 "world" 顯示的轉換成 string 或者 Sales_data:
1 cnt.combine(string("world")); 2 cnt.combine(Sales_data("world")); 3 cnt += string("world"); 4 cnt += Sales_data("world");
類型轉換函數不總是有效:
是否需要從 string 到 Sales_data 的轉換依賴於我們對用戶使用該轉換的看法。在上面的例子中,這種轉換可能是對的。 null_book 中的 string 可能表示了一個不存在的 isbn 編號。
但是如果將一個 istream 對象 cin 轉換為 Sales_data 的話,顯然不是我們想要的結果:
1 #include <iostream> 2 using namespace std; 3 4 class Sales_data{ 5 friend std::istream &read(std::istream&, Sales_data&); 6 7 //數據成員 8 private: 9 std::string book_no; 10 unsigned units_sold = 1; 11 double revenue = 1.0; 12 13 public: 14 Sales_data() = default;//不接受任何實參,默認構造函數 15 Sales_data(const std::string &s): book_no(s){}//只有一個形參,類型轉換構造函數 16 Sales_data(const std::string &s, unsigned n, double p): book_no(s), units_sold(n), revenue(p * n){} 17 Sales_data(std::istream&);//只有一個形參,類型轉換構造函數 18 19 void out_put(std::ostream &os){ 20 os << book_no << " " << units_sold << " " << revenue << std::endl; 21 } 22 23 Sales_data& combine(const Sales_data&); 24 }; 25 26 Sales_data& Sales_data::combine(const Sales_data &rhs){ 27 book_no += rhs.book_no; 28 units_sold += rhs.units_sold; 29 revenue += rhs.revenue; 30 return *this; 31 } 32 33 std::istream &read(std::istream&, Sales_data&); 34 35 Sales_data::Sales_data(std::istream &is){ 36 read(is, *this); 37 } 38 39 istream &read(istream &is, Sales_data &item){ 40 double price = 0; 41 is >> item.book_no >> item.units_sold >> price; 42 item.revenue = price * item.units_sold; 43 return is; 44 } 45 46 int main(void){ 47 Sales_data cnt("hello"); 48 cnt.combine(cin); 49 cnt.out_put(cout); 50 return 0; 51 }
cnt.combine(cin);先執行轉換構造函數
Sales_data::Sales_data(std::istream &is)
將 cin 轉換成 Sales_data 類型,因為該函數體中又調用了 read 函數,所以會產生輸入。構造的臨時 Sales_data 對象的初始化數據由輸入數據產生。因此最終輸出:helloworld 2 2
抑制構造函數定義的隱式轉換:
我們可以通過將構造函數聲明為 explicit 阻止構造轉換函數的隱式轉換:
1 #include <iostream> 2 using namespace std; 3 4 class Sales_data{ 5 friend std::istream &read(std::istream&, Sales_data&); 6 7 //數據成員 8 private: 9 std::string book_no; 10 unsigned units_sold = 1; 11 double revenue = 1.0; 12 13 public: 14 Sales_data() = default;//不接受任何實參,默認構造函數 15 explicit Sales_data(const std::string &s): book_no(s){}//加了explicit關鍵字 16 Sales_data(const std::string &s, unsigned n, double p): book_no(s), units_sold(n), revenue(p * n){} 17 explicit Sales_data(std::istream&);//加了explicit關鍵字 18 19 void out_put(std::ostream &os){ 20 os << book_no << " " << units_sold << " " << revenue << std::endl; 21 } 22 23 Sales_data& combine(const Sales_data&); 24 }; 25 26 Sales_data& Sales_data::combine(const Sales_data &rhs){ 27 book_no += rhs.book_no; 28 units_sold += rhs.units_sold; 29 revenue += rhs.revenue; 30 return *this; 31 } 32 33 std::istream &read(std::istream&, Sales_data&); 34 35 Sales_data::Sales_data(std::istream &is){ 36 read(is, *this); 37 } 38 39 istream &read(istream &is, Sales_data &item){ 40 double price = 0; 41 is >> item.book_no >> item.units_sold >> price; 42 item.revenue = price * item.units_sold; 43 return is; 44 } 45 46 int main(void){ 47 Sales_data cnt("hello"); 48 // cnt.combine(cin);//error 49 // cnt.combine(string("world"));//error 50 return 0; 51 }
加了 explicit 關鍵字後 cnt.combine(cin);cnt.combine(string("world"));都是錯誤的。
explicit 關鍵字只對一個實參的構造函數有效。需要多個實參的構造函數不能用於執行隱式轉換,所以無需將需要多個實參的構造函數聲明成 explicit 的,雖然這樣做也並沒有語法錯誤。explicit 只能在類內聲明構造函數時使用,在外部定義時不應該重復。
explicit 關鍵字聲明的構造函數只能用於直接初始化:
Sales_data cnt(null_book);
Sales_data gel = null_book;
如果沒有將對應的構造函數聲明成 explicit 的話,這兩者初始化方式都是可以的,反之則只有值即初始化是正確的,而拷貝初始化是錯誤的。
為轉換而顯示的使用構造函數:
盡管編譯器不會將 explicit 的構造函數用於隱式的轉換,但是我們可以這樣的構造函數顯示地強制進行轉換:
1 #include <iostream> 2 using namespace std; 3 4 class Sales_data{ 5 friend std::istream &read(std::istream&, Sales_data&); 6 7 //數據成員 8 private: 9 std::string book_no; 10 unsigned units_sold = 1; 11 double revenue = 1.0; 12 13 public: 14 Sales_data() = default;//不接受任何實參,默認構造函數 15 explicit Sales_data(const std::string &s): book_no(s){}//加了explicit關鍵字 16 Sales_data(const std::string &s, unsigned n, double p): book_no(s), units_sold(n), revenue(p * n){} 17 explicit Sales_data(std::istream&);//加了explicit關鍵字 18 19 Sales_data &operator+=(const Sales_data &it){ 20 book_no += it.book_no; 21 units_sold += it.units_sold; 22 revenue += it.revenue; 23 return *this; 24 } 25 26 void out_put(std::ostream &os){ 27 os << book_no << " " << units_sold << " " << revenue << std::endl; 28 } 29 30 Sales_data& combine(const Sales_data&); 31 }; 32 33 Sales_data& Sales_data::combine(const Sales_data &rhs){ 34 book_no += rhs.book_no; 35 units_sold += rhs.units_sold; 36 revenue += rhs.revenue; 37 return *this; 38 } 39 40 std::istream &read(std::istream&, Sales_data&); 41 42 Sales_data::Sales_data(std::istream &is){ 43 read(is, *this); 44 } 45 46 istream &read(istream &is, Sales_data &item){ 47 double price = 0; 48 is >> item.book_no >> item.units_sold >> price; 49 item.revenue = price * item.units_sold; 50 return is; 51 } 52 53 int main(void){ 54 string cnt = "world"; 55 Sales_data gel("hello"); 56 gel.combine(Sales_data(cnt));//顯示地強制類型轉換 57 gel += Sales_data(cnt); 58 return 0; 59 }
類5(轉換構造函數)