当我们在函数声明中型参类型与我们输入的实参类型不一致时编译器会依据怎样的原则进行处理呢。首先是将实参进行类型转换,若类型转换失败,则编译器会进行错误提示。进行类型转换时需要使用类型转换函数,那根据怎么样顺序来找出合适的函数呢?这个顺序的规则是非常复杂的,但并不是说因为复杂我们就无法判断了。我们来让代码说话。
class A
{
public:
//该类拥有一个只有一个参数的构造函数
A(int i)
{
i_ = i;
j_ = 10;
}
//重载 == 运算符,为了简就直接返回真了
bool operator==(const A& a)
{
return true;
}
private:
int i_ ;
int j_;
};
int main()
{
A a(10);
B b(20);
if(a == b[1])
return 0;
else
return 1;
}
程序不仅非常之短,而且简单到不屑一看的地步。那程序输出的结果是什么?等一下,能这样比吗 (if(a == b[1]))?是不是程序员写错了?根据我们常识要不是 if(a == b) 要不就是 if(a[2] == b[2]) ,不管它,先编译一下试试。居然通过了!这个时候是不是会觉得编译器有 BUG ,呵呵。
我们在A中重载了 == 运算符,那么此处的 == 就要去调用 A 的 == 函数。这一点是没有错的,编译器也按照我们脑海中的线路走。但是参数类型不一样,b[2] 返回的是 INT 类型,但是==函数需要的是const A& 类型,于是编译器就“私自”进行了(隐式)转换,把 INT 转换成 const A& 。此时巧了,A 正好有一个“只有一个参数”的构造函数,于是转换成功了,编译也通过了。其实写成if(a == b[2])有可能只是我们的一个笔误,但是编译器却理解成了另外一种意思表达出来就是 if(a == static_cast<A>(b[2])) 。我们的这个例子代码太简单了所以看不出这样做的危害来,如果类 A 中还有其他成员变量,构造函数中还有其它操作的话,那麻烦可就大了。至于效率上,每次转换的时候都要生成并销毁一个临时的 A 对象,如果构造A的构造函数比较复杂在如果这个条件判断实在一个循环中... ... 呵呵,效率也就可想而知了。
当一个类有一个“只有一个参数”的构造函数时就有可能会出现此种情况,而且这种错误在一个大的项目中并不容易找出来。所以我们可以尽量避免“只有一个参数”的构造函数,“避免”并不等于“绝对”,这就需要我们靠丰富的实战经验来把握这个度了。了解这么一个“规则”之后,相信大家在写代码、找 BUG 中应该会多了一种方案和思路了。
当然,比我们“牛”的人大有人在!牛人们早就想到这一点了,于是在 C++ 中有了这么一个关键字 explicit 。把构造函数 A(int i) 写成 explicit A(int i) 就 OK 了,是不是真的很“牛”。