自动应用类型转换
程序清单11.21将int(poppins)和cout 一起使用。假设省略了显式强制类型转换:
cout <<"Poppins:"<<poppins<"pounds.\n";
程序会像在下面的语句中那样使用隐式转换吗?
double pwt = poppins;
答案是否定的。在p_wt示例中,上下文表明,poppins应被转换为 double 类型。但在 cout 示例中,并没有指出应转换为 int 类型还是 double 类型。在缺少信息时,编译器将指出,程序中使用了二义性转换。该语句没有指出要使用什么类型。
有趣的是,如果类只定义了double转换函数,则编译器将接受该语句。这是因为只有一种转换可能,
因此不存在二义性。
赋值的情况与此类似。对于当前的类声明来说,编译器将认为下面的语句有二义性而拒绝它。
long gone=poppins;//ambiguous
在 C++中,int 和 double 值都可以被赋给 long 变量,所以编译器使用任意一个转换函数都是合法的。编译器不想承担选择转换函数的责任。然而,如果删除了这两个转换函数之一,编译器将接受这条语句。例如,假设省略了 double 定义,则编译器将使用int转换,将 poppins 转换为一个int 类型的值。然后在将它赋给 gone 时,将int类型值转换为long类型。
当类定义了两种或更多的转换时,仍可以用显式强制类型转换来指出要使用哪个转换函数。可以使用下面任何一种强制类型转换表示法:
long gone =(double)poppins;//use double conversion
long gone =int(poppins)i;//use int conversio
第一条语句将 poppins 转换为一个 double 值,然后赋值操作将该 double 值转换为 long 类型。同样,第二条语句将 poppins 首先转换为 int类型,随后转换为long。
和转换构造函数一样,转换函数也有其优缺点。提供执行自动、隐式转换的函数所存在的问题是:在用户不希望进行转换时,转换函数也可能进行转换。例如,假设您在睡眠不足时编写了下面的代码:
int ar[20];
::.
Stonewt temp(14,4);
:
int Temp=l;
cout<<ar[temp]<<"!\n";//usedi temp instead of Temp
通常,您以为编译器能够捕获诸如使用了对象而不是整数作为数组索引等错误,但Stonewt 类定义了-个 operator int(),因此 Stonewt对象temp将被转换为 int200,并用作数组索引。原则上说,最好使用显式转换,而避免隐式转换。在 C++98中,关键字 explicit 不能用于转换函数,但 C++11 消除了这种限制。因此,在C++11中,可将转换运算符声明为显式的:
class Stonewt
//conversion functions
explicit operator int()const;
explicit operator double()const;
有了这些声明后,需要强制转换时将调用这些运算符。另一种方法是,用一个功能相同的非转换函数替换该转换函数即可,但仅在被显式地调用时,该函数才会执行。也就是说,可以将:
Stonewt::operator int()freturn int(pounds +0.5);
替换为:
int Stonewt::Stone to Int(){return int(pounds +0.5);
这样,下面的语句将是非法的:
int plb=poppins;
但如果确实需要这种转换,可以这样做:
int plb=poppins.Stone to Int();
警告:应谨慎地使用隐式转换函数。通常,最好选择仅在被显式地调用时才会执行的函数。
总之,C++为类提供了下面的类型转换。
1,只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型。例如,将int值赋给Stonewt 对象时,接受 imt参 数的 Stonewt 类构造函数将自动被调用。然而,在构造函数声明中使用 explicit 可防止隐式转换,而只允许显式转换。
2,被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。转换函数是类成员,没有返回类型、没有参 数、名为operator typeName(),其中,typeName 是对象将被转换成的类型将类对象赋给typeName 变量或将其强制转换为 typeName 类型时,该转换函数将自动被调用。
11.6.2 转换函数和友元函数
下面为 Stonewt 类重载加法运算符。在讨论 Time 类时指出过,可以使用成员函数或友元函数来重载加法。(出于简化的目的,我们假设没有定义
operator double()转换函数。)可以使用下面的成员函数实现加法:
Stonewt Stonewt::operator+(const Stonewt&st)const
{
double pds =pounds +st.pounds;
Stonewt sum(pds);
return sum;
}
也可以将加法作为友元函数来实现,如下所示:
Stonewt operator+(const Stonewt &stl,const Stonewt & st2)double
{
pds =stl.pounds +st2.pounds;
Stonewt sum(pds);
return sum;
}
别忘了,可以提供方法定义或友元函数定义,但不能都提供。上面任何一种格式都允许这样做:
Stonewtjennyst(9,12);
bennySt(12,8);Stonewt
Stonewttotal;
total =jennySt +bennySt;
另外,如果提供了 Stonewt(double)构造函数,则也可以这样做:
Stonewt jennySt(9,12);
double kennyD =176.0;
Stonewt total;
total=jennySt + kennyD;
但只有友元函数才允许这样做:
Stonewt jennySt(9,12);
double pennyD=146.0;
Stonewt total;
total =pennyD +jennySt;
为了解其中的原因,将每一种加法都转换为相应的函数调用。首先:total =jennySt +bennySt;
被转换为:
total=jennySt.operator+(bennySt);//member function或:
total =operator+(jennySt,bennySt);//friend function
上述两种转换中,实参的类型都和形参匹配。另外,成员函数是通过Stonewt对象调用的。其次:
total =jennySt +kennyD;
被转换为:
total =jennySt.operator+(kennyD);// member function
或:
total =operator+(jennySt,kennyD);//friend function
同样,成员函数也是通过 Stonewt对象调用的。这一次,每个调用中都有一个参数(kennyD)是 double
类型的,因此将调用 Stonewt(double)构造函数,将该参数转换为Stonewt对象。另外,在这种情况下,如果定义了operator double()成员函数, 将造成混乱,因为该函数将提供另一种解释方式。编译器不是将 kennyD转换为 double 并执行 Stonewt加法,而是将jennySt转换为 double 并执行double 加法。过多的转换函数将导致二义性。
最后:
total =pennyD +jennySt;
被转换为:
total =operator+(pennyD,jennySt);//friend function
其中,两个参数都是 double 类型,因此将调用构造函数 Stonewt(double),将它们转换为 Stonewt 对象然而,不能调用成员函数将jennySt和peenyD相加。将加法语法转换为函数调用将类似于下面这样:
total =pennyD.operator+(jennySt);//not meaningful
这没有意义,因为只有类对象才可以调用成员函数。C++不会试图将pennyD转换为 Stonewt对象。将对成员函数参数进行转换,而不是调用成员函数的对象。
这里的经验是,将加法定义为友元可以让程序更容易适应自动类型转换。原因在于,两个操作数都成为函数参数,因此与函数原型匹配。
实现加法时的选择
要将 double 量和 Stonewt量相加,有两种选择。第一种方法是(刚介绍过)将下面的函数定义为友元
函数,让 Stonewt(double)构造函数将 double 类型的参数转换为 Stonewt 类型的参数:
operator+(const Stonewt&,const Stonewt&)
第二种方法是,将加法运算符重载为一个显式使用double类型参数的函数:
```cpp
Stonewt operator+(double x);//member function
friend Stonewt operator+(double x,Stonewt& s);
```cpp
total=jennySt+kennyD;//Stonewt+double
而下面的语句将与友元函数
```cpp
operator+(double x,Stonewt&s)
```cpp
total =pennyD +jennySt;//double + Stonewt
前面对 Vector 乘法做了类似的处理。每一种方法都有其优点。第一种方法(依赖于隐式转换)使程序更简短,因为定义的函数较少。这也意味程序员需要完成的工作较少,出错的机会较小。这种方法的缺点是,每次需要转换时,都将调用转换构造函数,这增加时间和内存开销。第二种方法(增加一个显式地匹配类型的函数)则正好相反。它使程序较长,程序员需要完成的工作更多,但运行速度较快。
如果程序经常需要将 double值与 Stonewt对象相加,则重载加法更合适;如果程序只是偶尔使用这种加法,则依赖于自动转换更简单,但为了更保险,可以使用显式转换。

被折叠的 条评论
为什么被折叠?



