目录
转换构造函数 (265页)
● 我们可以为类定义隐式转换规则。如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数!
●注意: 能通过一个实参调用的构造函数定义了一条从构造函数实参类型到类类型隐式转换的规则 。
class Sales_data
{
public:
Sales_data() = default;
Sales_data(const string &s)
{
bookNo = s;
cout << "把 string 类型的值转换为该类类型的构造函数被调用!" << endl;
}
Sales_data &combine(const Sales_data & );
Sales_data(istream &is)
{
double price = 0;
is >> bookNo >> units_sold >> price;
revenue = price * units_sold;
}
void show()
{
cout << "分别显示它们的值为:" << bookNo << " " << units_sold << " " << revenue << endl;
}
private:
string bookNo;
unsigned units_sold = 55;
double revenue = 66.6;
};
Sales_data &Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold; //把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
bookNo += rhs.bookNo;
return *this; //返回调用该函数的对象
}
int main()
{
Sales_data item;
string null_book = "TAOTAO";
//构造一个临时的Sales_data对象
item.combine(null_book);
item.show();
// item.combine("9 - 999 - 99999 - 9"); //错误,需要进行两次转换,第一次是转换成string,再把这个临时的string 转换成 Sales_data
//正确:显示地转换成string,隐式地转换成Sales_data
item.combine(string("9-999-99999-9"));
//正确:显示地转换成Sales_data,隐式地转换成string
item.combine(Sales_data("9-999-99999-9"));
Sales_data item1(null_book); //正确:直接初始化
//Sales_data item2 = null_book; //错误:不能将explicit构造函数用于拷贝形式的初始化过程,explicit构造函数只能用于直接初始化
system("pause");
return 0;
}
● 在上述程序中,有两个构造函数只有一个参数的,它们分别定义了从这两种类型向Sales_data 隐式转换的规则。 也就是说,在需要使用Sales_data的地方, 我们可以使用 string 作为替代。
● 在主函数中我们用一个string实参调用了combine 函数, 该调用是合法的, 编译器用给定的string自动创建了一个Sales_data 对象。 新生成的这个(临时) Sales_data对象被传递给combine。 因为combine 的参数是一个常量引用, 所以我们可以给该参数传递一个临时量。
● 注意: 编译器只会自动地执行一步类型转换。不可以连续使用两种转换机制, 看上述代码主函数
● 关键字explicit 只对一个实参的构造函数有效。 需要多个实参的构造函数不能用于指向隐式转换,所以无须将这些构造函数指定为explicit的, 只能在类中声明构造函数时使用explicit关键字, 在类外部定义时不应重复。
此时, 没有任何构造函数能用于隐式地创建Sales data对象, item.combine(null_book); 这样写就语法错误。
● 注意 : 发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时我们只能使用直接初始化而不能使用explicit 构造函数(看上述的主函数)
所以当我们使用explicit 关键字声明构造函数时,它将只能以直接初始化的形式使用。 而且,编译器将不会在自动转换过程中使用该构造函数。
为转换显式地使用构造函数
●尽管编译器不会将explicit的构造函数用于隐式转换过程, 但是我们可以使用这样的构造函数显式地强制进行转换。
item.combine(Sales_data(null_book)); //正确,实参是一个显式构造的Sales_data对象
item.combine(static_cast<Sales_data>(cin)); //正确,static_cast可以使用explicit的构造函数
标准库中含有显式构造函数的类
我们用过的一些标准库中的类含有单参数的构造函数:
接受一个单参数的const char*的string构造函数(参见3.2.1节,第76页) 不是explicit的。
接受一个容量参数 的vector构造函数(参见3.3.1节,第87页)是explicit的。
聚合类
● 聚合类使得用户可以直接访问其成员, 当一个类满足如下条件时,我们说它是聚合的:
所有成员都是`public`的
没有定义任何的构造函数
没有类内初始值
没有聚类,也没有`virtual` 函数。
struct Data
{
int ival;
string s;
};
int main()
{
Data vall = { 11,"huang" }; // 使用初始值列表初始化聚合类的成员
// 还可以分别初始化它们的成员
vall.ival = 0;
vall.s = string("Anna");
Data val = { 1,"TT" }; //使用花括号括起来的成员初始值列表,来初始化聚合类的数据成员
Data val = { "TT",1 }; // 错误:初始值的顺序必须与数据成员声明的顺序一致
system("pause");
return 0;
}
初始值的顺序必须与声明的顺序一致,也就是说,第一个成员的初始值要放在第一个,然后是第二个,以此类推。下面的例子是错误的:
Data vall = { “sdsd” , 44 }; //错误
● 与初始化数组元素的规则一样,如果初始值列表中的元素个数少于类的成员数量, 则靠后的成员被值初始化。 初始值列表的元素个数绝不能超过类的成员数量