12.4.4 隐式类类型转换
可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。
class Class1
{
public:
int counter;
public:
Class1(int counter = 100):counter(counter){}
};
class Class2
{
public:
int counter;
public:
Class2(Class1 c1 = Class1()):counter(c1.counter){}
};
Class2 c2(200);
cout << c2.counter << endl;
1. 抑制由构造函数定义的隐式转换
可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数。
explicit关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的定义上不再重复它。
当构造函数被声明为explicit时,编译器将不使用它作为转换操作符。
2. 为转换而显式地使用构造函数
class Class1
{
public:
int counter;
public:
explicit Class1(int counter = 100):counter(counter){}
};
class Class2
{
public:
int counter;
public:
Class2(Class1 c1 = Class1()):counter(c1.counter){}
};
Class2 c2(Class1(200));
cout << c2.counter << endl;
显式使用构造函数只是中止了隐式地使用构造函数。任何构造函数都可以用来显式地创建临时对象。
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显式地构造对象。
12.4.5 类成员的显式初始化
尽管大多数对象可以通过运行适当的构造函数进行初始化,但是直接初始化简单的非抽象类的数据成员仍是可能的。对于没有定义构造函数并且全体数据成员均为public的类,可以采用与初始化数组相同的方式初始化其成员。
class Class3
{
public:
int &i;
string *str;
};
int i = 10;
string j = "20A";
string *k = &j;
Class3 c3 = {i, k};
cout << c3.i << endl;
cout << *c3.str << endl;
根据数据成员的声明次序来使用初始化式。
这种形式的初始化从C继承而来,支持与C程序兼容。显式初始化类类型对象的成员有三个重大的缺点。
(1) 要求类的全体数据成员都是public。
(2) 将初始化每个对象的每个成员的负担放在程序员身上。这样的初始化是乏味且易于出错的,因为容易遗忘初始化式或提供不适当的初始化式。
(3) 如果增加或删除一个成员,必须找到所有的初始化并正确更新。
定义和使用构造函数几乎总是较好的。当我们为自己定义的类型提供一个默认构造函数时,允许编译器自动运行那个构造函数,以保证每个类对象在初次使用之前正确地初始化。