一、转换构造函数
当一个类A的构造函数,其参数列表仅存在一个其他类型B的参数(或者剩余参数存在默认实参),那么该构造函数被称之为转换构造函数。 该构造函数可用于将类型B转换为类型A,并且在参数传递、拷贝赋值等场景下,可以由编译器直接完成这一步隐式转换。
class Info{
private:
String name;
public:
A(String name_): name(name_){};
};
String str = "Mollia";
Info info = str; // 成功
Info info = "Mollia"; // 失败,仅支持单步隐式转换
void test(Info info);
test(str); // 成功调用
test("Mollia"); // 错误,仅支持单步隐式转换
test(Info(str)); // 可以手动进行类类型转换
请注意:编译器仅可利用转换构造函数做出单步隐式转换。指的是如果C可以隐式转换为B,而B可隐式转换为A类型,那么编译器无法直接将C转换为A类型。但这不影响我们显式调用构造函数做出转换。
二、使用explicit关键字抑制隐式转换
有些情况下,由编译器自动执行的隐式转换可能与预期功能不符,此时可以将上述的转换构造函数使用explicit
关键字进行修饰,这类构造函数具备以下特点:
- 无法类类型隐式转换,但是仍然可以手动显式地执行转换
- 只能用于直接初始化,无法用于拷贝初始化
class A{
private:
string name;
public:
A() = default;
explict A(string name_): name(name_){};
};
void test(A &obj); // a function defination called `test`
test(string(“ben”)); // 错误: explicit关键字修饰的构造函数无法对参数类型对象进行隐式类型转换
test(A(“ben”)); // 正确:但是不影响我们显式调用进行转换
A obj1; // 默认构造
A obj2 = obj1; // 错误:explicit关键字修饰的构造函数无法被用于拷贝初始化
A obj3(“name”); // 而只能使用直接初始化
绝佳例子:
标准库类型string具备一个单参数的const char* 类型的非explicit
构造函数,因此我们可以直接:string a = "aaa";
。与之相反地,vector有一个单参数explicit
构造函数,参数指定了初始容量大小,因此无法通过隐式类型转换得到,这就是explicit使用的一个正确场景,因为将一个数字隐式转化成一个vector
这件事情听起来就很炸裂。