转换构造函数
如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作为转换构造函数。
实例
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal(string name):m_name(name) {}
// 比较动物的名字
bool isSame(Animal animal)
{
return m_name == animal.m_name;
}
private:
string m_name;
};
int main()
{
Animal dog("dog");
// 打印为 1
cout << dog.isSame(string("dog")) << endl;
getchar();
return 0;
}
在示例中定义接受 string 的构造函数就是定义了从 string 类型向Animal隐式转换的规则。编译器用给定的 string 参数自动创建了一个 Animal对象,新生成的这个临时 Animal 对象被传递给 isSame,然后进行比较。
只允许一步类类型转换
编译器只会自动地执行一步类型转换。如果我们把上例中的dog.isSame(string(“dog”)) 换成 dog.isSame(“dog”), 那么编译器就会报错:
“不存在从 “const char [4]” 转换到 “Animal” 的适当构造函数”,因为它经历了两步转换。
- 从 “dog”转换成 string
- 从 string转换成Animal
因此我们可以这样做:
// 显示地转换成 string,隐式地转换成 Animal
dog.isSame(string("dog"));
// 或者
// 隐式地转换成 string, 显示地转换成 Animal
dog.isSame(Animal("dog"));
隐式转换的副作用
有时隐式转换会带来很大的副作用,比如,标准库中的 vector 的接受一个容量参数的构造函数是禁止隐式转换的,如果不禁止,那么
std::vector a(5)
可声明一个初始长度为5的vector,但是这样写
std::vector a=5
就也是可以的了。极大地降低了可读性。
禁止隐式转换
我们可以将构造函数声明为 explicit 来禁止隐式转换。而且 explicit 构造函数只能用于直接初始化即当我们执行拷贝形式的初始化时(使用=)时不允许的。
如果我们不加 explicit:
Animal dog("dog"); // 正确
Animal cat = "cat"; // 正确 发生隐式类类型转换
dog.isSame(string("dog")); // 正确 发生隐式类类型转换
如果我们加上 explicit:
Animal dog("dog"); // 正确
Animal cat = "cat"; // 错误 不允许隐式类类型转换
dog.isSame(string("dog")); // 错误 不允许隐式类类型转换
// 可以显示的进行类类型转换
Animal cat = Animal("cat"); // 正确
dog.isSame(Animal("dog")); // 正确
所以标准库中的 vector 的接受一个容量参数的构造函数是禁止隐式转换的,声明了构造函数为 explicit。
关键字 explicit 只对一个实参的构造函数有效。需要多个实参的构造函数不能用于隐式转换,所以无须将这些构造函数指定为 explicit 的。只能在类内声明构造函数时使用 explicit 关键字,在类外部定义时不应重复。
参考 《C++ primer》第五版