转换构造函数:
当一个构造函数只有一个参数,而且该参数又不是本类的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 double revenue = 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 18 Sales_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 }