5. 对定制的“类型转换函数”保持警觉
C++中允许编译器在不同类型中执行隐式转化,例如默默地将char转化为int,将short转化为double等等,这些是语言提供的。现在当你写自己的类型时,你可以选择是否提供某些函数,供编译器用作隐式类型转化之用。如定义一个类类型,是否允许其它类型转化为此类类型,我们可以操控的。
可以通过两种函数允许编译器执行这样的隐式转化:单自变量constructor 和隐式类型转化操作符。
单自变量constructor :指的是以单一自变量成功调用的constructor,这样的constructor可能只有一个单一参数,也可能有多个参数,而其他参数都有默认值。如下面两个例子:
class CName{
public:
CName(const string& s); //ctor声明时只有一个自变量
//...
};
class CRational{
public:
CRational(int numerator = 0, int denominator = 1); //ctor声明时有两个自变量,但其他有默认值
//...
};
//上面的ctor可以用来作为隐式类型转化函数
CName stu = "hazirguo"; //可以将string类型转化为CName类型
CRational doub = 3; //可以将int 类型转化为CRational类型
隐式类型转化操作符:这是一个成员函数,由关键词operator 后加上一个类型名称。如下例:
class CRational{
public:
CRational(int numerator, int denominator);
operator double() const //可以完成将CRational类转化为double类型
{ return numerator * 1.0 / denominator; }
//...
};
//下面的代码会调用上面的转化函数
CRational r(1, 2); // r = 1/2
double d = 0.5 * r; // 将r转化为double,再相乘
但是,最好不要提供任何类型转化函数!!!因为在你从未预期的情况下,此类函数就可能被调用,而结果显然不是你想要的,但是此类错误往往是很难调试的。分别举例说明之:
例一:
class CArray{
public:
CArray(int lowBound, int highBound); //两个参数的ctor,不可能作为隐式转化函数
CArray(int size); //单变量ctor,可能作为隐式转化函数使用
int operator[] (int index);
//...
};
bool operator == (const CArray& lhs, const CArray& rhs); //重载 == 函数
CArray a(10);
CArray b(10);
for (int i = 0; i < 10; i++)
{
if (a == b[i]) //原意是a[i] == b[i],但此处编译器却为报错
{ /* do something when a[i]==b[i] */ } // (***)
else
{ /* ... */ }
}
之所以没有报错的原因是:a == b[i]的一边是CArray型,一边是int型,而重载operator == 函数带有两个CArray型参数,编译器试图将int转化为CArray型是行的通的,
因为CArray有个单自变量的ctor,可以将int转化为CArray型。但很显然,这样的话,结果不是我们预期的那样!
解决的方法:只要将ctor声明为explicit就行了,编译器便不能因隐式转化的需要调用它们。不过显示类型转化仍然是可行的:
class CArray{
public:
//...
explicit CArray(int size); //这里使用了关键字explicit
//...
};
CArray a(10);
CArray b(10);
if (a == b[i]) //error! 无法将int隐式转化为CArray型
if (a == static_cast<CArray> (b[i])) //right!显示转化仍然可行
例二:
class CRational{
public:
CRational(int numerator, int denominator);
operator double() const //可以完成将CRational类转化为double类型
{ return numerator * 1.0 / denominator; }
//...
};
//
CRational r(1, 2);
cout << r; //原意输出1/2,但却输出了0.5
上面程序并没有定义operator << 可以接受CRational,但编译器却没有报错,却输出了一个double型的数0.5。原因在于,编译器会想尽各种方法让函数转化成功,本例中只要调用CRational::operator double 即可将CRational转化为double型,于是上述代码将r以非分数形式输出。显然,这也不是我们想要的结果!!
解决方法:以功能对等的另一个函数取代类型转化操作符。本例中,为了将CRational转化为double,不妨以一个名为asDouble的函数取代operator double:
class CRational{
public:
CRational(int numerator, int denominator);
double asDouble() const ; //将CRational 转化为double
};
CRational r(1, 2);
cout << r; //error! CRational 没有operator <<
cout << r.asDouble(); //right!
小结: 一般而言,愈有经验的C++程序员愈可能避免使用类型转化操作符。
参考文献: 《More Effective C++ 35个改善编程与设计的有效方法 中文版
邮箱:haifenglinying#yahoo.cn (#->@)
个人主页:www.hazirguo.com