先看代码
#include <iostream>
#include <string>
using namespace std;
class Student
{
string name;
int number;
public:
Student(const string &str, int n = 1) : name(str), number(n) {}
Student() {}
void Print(const Student &s); //期待的是Student类的参数
};
void Student::Print(const Student &s)
{
if (s.name == name)
{
cout << "same person" << endl;
}
else
{
cout << "different" << endl;
}
}
int main()
{
Student a("lzj");
Student b("lb");
cout << "a == b ? : " <<endl;
a.Print(b);
cout << "a == a ? : " << endl;
a.Print(string("lzj")); //这里传递的是string类
// cout << "a == a ? : " << a.Print("lzj") << endl;
return 0;
}
输出结果为
a == b ? :
different
a == a ? :
same person
为什么呢? 明明第二个传递的是一个string类的参数,结果却是same
这是因为c++里,能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则(c++ primer P296)
也就是说,string类的实参被隐式转换成了Student类类型,而当上面的构造函数里的参数列表里,不去默认给n赋初值的话,必须传递两个实参的时候,也就不成立了
同时,编译器只允许一步类类型转换,意思也就是说,只会自动执行一步类型转换,其实当我们创建对象时,传递进去的“lzj”和“lb”都被隐式的从const char 转换成了string类,所以,被我注释掉的那条语句编译不会通过,因为只能一步的从const char 转换为string,而不能再次转换为类类型了
如何阻止:可以将构造函数声明为explicit加以阻止,此时没有任何构造函数可以隐式的创建类对象,explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换,无须指定为explicit。只能在类内声明构造函数时使用explicit,类外部定义时不应重复
而且,explicit构造函数只能用于直接初始化,不能用于拷贝形式的初始化过程。声明为explicit后,编译器将不会在自动转换过程中使用该构造函数,但是,虽然编译器不会将其使用于隐式转换过程,但我们仍然可以使用这样的构造函数显示的强制转换