只有接受一个参数(其他参数有默认值的也算)的构造函数才能作为转换构造函数。
在C++中,接受一个参数的构造函数为将类型与该参数相同的值转换为类提供了蓝图。因此,下面的构造函数用于将double类型的值转换为Stonewt类型:
1
|
Stonewt(double lbs) // double转Stonewt的模板
|
也就是说,可以编写这样的代码:
1
2
|
Stonewt myCat; // 创建一个Stonewt对象
myCat = 19.6; // 使用Stonewt(double)将19.6转换为Stonewt对象
|
这一过程称为隐式转换,因为它是自动进行的,而不需要显式强制类型转换。
将构造函数用作自动类型转换似乎是一项不错的特性。然而,当程序员拥有更丰富的C++经验时,将发现这种自动特性并非总是合乎需要的,因为这会导致意外的类型转换。因此,C++新增了关键字explicit,用于关闭这种自动特性。也就是说,可以这样声明构造函数:
1
|
explicit Stonewt(double lbs) // 不允许隐式转换
|
这将关闭上述示例中介绍的隐式转换,但仍然允许显式转换,即显式强制类型转换:
1
2
3
4
|
Stonewt myCat;
myCat = 19.6; // 不允许,因为Stonewt(double)声明为explicit
myCat = Stonewt(19.6); // OK,一个显示地转换
myCat = (Stonewt)19.6; // OK,原始地显示类型转换
|
编译器在什么时候将使用Stonewt(double)函数呢?
如果在声明中使用了关键字explicit,则Stonewt(double)将只用于显式强制类型转换,否则还可以用于下面的隐式转换:
- 将Stonewt对象初始化为double值时。如:Stonewt st(1.23);
- 将double值赋给Stonewt对象时。如:Stonewt st; st = 1.23;
- 将double值传递给接受Stonewt参数的函数时。如:display(Stonewt& st);-> display(1.23);
- 返回值被声明为Stonewt的函数试图返回double值时。如:Stonewt& go(){ return 1.23; }
- 在上述任意一种情况下,使用可转换为double类型的内置类型时。如:Stonewt& go() { return 123; }
可以将数字转换为Stonewt对象。可以做相反的转换吗?也就是说,是否可以将Stonewt对象转换为double值,就像如下所示的那样?
1
2
|
Stonewt wolfe(285.7);
double host = wolfe;// ??可以吗??
|
可以这样做,但不是使用构造函数。构造函数只用于从某种类型到类类型的转换。要进行相反的转换,必须使用特殊的C++运算符函数——转换函数。
转换函数是用户定义的强制类型转换,可以像使用强制类型转换那样使用它们。例如,如果定义了从Stonewt到double的转换函数,就可以使用下面的转换:
1
2
3
4
5
6
|
Stonewt wolfe(285.7);
double host = double (wolfe); // syntax #1
double thinker = (double) wolfe; // syntax #2
也可以让编译器来决定如何做:
Stonewt wells(20, 3);
double star = wells; // 隐式使用转换函数
|
编译器发现,右侧是Stonewt类型,而左侧是double类型,因此它将查看程序员是否定义了与此匹配的转换函数。(如果没有找到这样的定义,编译器将生成错误消息,指出无法将Stonewt赋给double。)
创建一个转换函数,转换为typeName类型,需要使用这种形式的转换函数:
operator typeName();// 无返回类型,无参数
请注意以下几点:
- 转换函数必须是类方法
- 转换函数不能指定返回类型
- 转换函数不能有参数
在类的头文件定义:
1
2
|
operator int() const;
operator double() const;
|
实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
......
// construct Stonewt object from stone, double values
Stonewt::Stonewt(int stn, double lbs) {
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
......
// conversion functions
Stonewt::operator int() const {
return int(pounds + 0.5);
}
Stonewt::operator double() const {
return pounds;
}
|
使用:
1
2
3
4
5
6
|
Stonewt poppins(9, 2.8);// 9 stone, 2.8 pounds
double p_wt = poppins;// implicit conversion
cout << "Convert to double => ";
cout << "Poppins: " << p_wt << " pounds.\n";
cout << "Convert to int => ";
cout << "Poppins: " << int(poppins) << " pounds.\n";
|
1
2
|
Convert to double => Poppins: 128.8 pounds.
Convert to int => Poppins: 129 pounds.
|
原则上说,最好使用显式转换,而避免隐式转换。在C++98中,关键字explicit不能用于转换函数,但C++11消除了这种限制。因此,在C++11中,可将转换运算符声明为显式的:
1
2
3
4
5
6
|
class Stonewt {
...
// 转换函数
explicit operator int() const;
explicit operator double() const;
};
|
有了explicit声明后,在显式强制转换时才调用这些运算符。
另一种方法是,用一个功能相同的非转换函数替换该转换函数即可,但仅在被显式地调用时,该函数才会执行。也就是说,可以将:
1
2
3
|
Stonewt::operator int() { return int (pounds + 0.5); }
替换为:
int Stonewt::Stone_to_Int() { return int (pounds + 0.5); }
|
这样,下面的语句:
1
2
3
4
|
int plb = poppins; // 非法
int plb1 = (int) poppins;// 合法
int plb2 = int(poppins);// 合法
int plb3 = poppins.Stone_to_Int(); // 合法
|
应谨慎地使用隐式转换函数。通常,最好选择仅在被显式地调用时才会执行的函数。
|