隐式类型转换(构造函数的隐式调用)
先看一下隐式类型转换如何发生吧:
#include <iostream>
using namespace std;
class point {
public:
int x,y;
point(int x=0, int y=0):x(x),y(y) {}
}
void displayPoint(const point &p)
{
cout << "( " << p.x << "," << p.y << " )";
}
int main()
{
displayPoint(1);
point p = 1;
}
我们定义了一个很简单的point
类,它的构造函数使用了默认参数(int x=0, int y=0
),这时main函数里的两句都触发了构造函数的隐式调用
如果构造函数不使用默认参数,会在编译时报错
很显然,函数displayPoint
需要的是point
类型的参数,而我们传入一个int
数据,这个程序能够成功运行,并且成功输出(0,0)
说明隐式调用了构造函数。另外需要注意的是,在对象刚刚定义的时候,使用赋值操作符=
,调用的是构造函数,而不是operator=
运算符
这种悄然发生的事情,有时可以带来便利,让你的程序可以继续运行,而更多的会带来很多意想不到的结果,explict
关键字用来避免这样的情况发生
explicit 关键字
c++11中对explicit的解释为指定构造函数或转换函数(operator type()
)为显示,即它不能用于隐式转换和复制初始化
在上面point
示例中,构造函数被explicit
修饰后,就不能被隐式调用了,主函数中的两句声明定义将不会通过编译
能用就用
如果我们能预料某种情况的发生,就不要把这个情况控制权交给编译器。即使没有加explicit
的构造函数,我们也应该先声明point p(1)
,再把p作为参数传递给displayPoint
函数
Effective C++中也写到
被声明为
explicit
的构造函数通常比non-explicit
兄弟更受欢迎,因为它们禁止编译器执行非预期(往往也不被期望)的类型转换,除非我有一个好理由允许构造函数被用于隐式类型转换,否则我会把它声明为explicit
,我鼓励你遵循同样的政策
operator用于隐式类型转换
C++中的operator
主要有两种作用,一是操作符重载,二是自定义对象类型的隐式转换。在上面提到的point
类中,我们提到如果构造函数没有默认参数,隐式转换的时候就会编译出错,但是如果我们利用operator
的第二个作用,就可以自定义隐式转换类型。
#include <iostream>
#include <string>
using namespace std;
class point {
public:
point(int x, int y):x(x),y(y) {
cout << "constructor" << endl;
}
bool operator()(int x, int y) {
cout << "opearator overload" << endl;
return x>this->x && y>this->y;
}
operator string() {
cout << "type convert" << endl;
string str = "(" + x;
str += "," + y;
str += ")";
return str;
}
private:
int x,y;
};
int main()
{
// part1
point p1(5,6);
if(p1(10,9)){
cout << "point(10,9) is larger than point(5,6)" << endl;
}
// part2
string str(p1);
cout << str;
}
运行结果如下:
主函数的part1调用的是operator的重载作用,我们主要探讨part2的隐式转换作用。我们往string
类的构造函数中传递的point
类,显然string
类中必然没有以point
类为参数的构造函数,但是从运行结果来看,这次构造是成功的。
如果string
类有默认参数的构造函数,那么string str(p1)
会隐式调用该默认参数的构造函数来构造string。对于没有默认参数的构造函数情况下,类的隐式类型转换,是使用一个其他类型构造当前类的临时对象,并用此临时对象来构造当前对象,这种转换必须有其他类型的构造函数支持;而operator
算子的隐式类型转换,就是使用当前类型对象生成另一个类型的对象(正好与没有默认参数构造函数情况下,类的隐式转换相反),这种转换必须有operator
算子的支持。
在这个例子中,string str(p1)
是将point
类对象转换成string
类对象,使用的是string
类的隐式构造(转换),string
类不存在有默认参数的构造函数,也不存在以point
类为参数的构造函数,那么string
类就需要去point
类中去找以operator
算子修饰的将point
类转为string
类的转换函数,并调用该函数,最终返回的string
临时对象,被string
类用来拷贝构造成一个新的string
对象str
参考文章
C++ explicit 关键字