类型转化
来源:互联网 发布:外围服务 知乎 编辑:程序博客网 时间:2024/04/28 04:43
以前我们提过,当一个类的非explicit构造函数接受一个实参时,如果你在使用该类的地方填入的是那个实参类型,就会发生隐式转化:通过实参临时创建一个该类的对象,然后把这个对象填入使用该类的地方。其实我们也可以自己定义类型的转化。有人可能觉得这件事情不是很有意义,其实,在某些地方,它的使用往往能起到事半功倍的效果。举一个例子说明,加入我们定义了一个“较小”的整数范围0~255,把它封装成一个类。(在图像处理中,8bit灰度图像的像素就是这个范围的)。对像素的处理肯定是要定义加减乘除,大于小于,大于等于,小于等于,等于,不等之类的操作,这意味我们要为每个操作重载一个运算符。而且还要支持这些运算符的操作数不仅是我们自己的类,也有可能是一般的整数,或者浮点数,这意味着我们还要重载这些运算符。虽然可能难度不大,但是如此繁琐的操作也足以让人抓狂。
那么换一个思路就能解决这个问题:我们定义一个从我们的类类型到int的转化操作,这样的话所有跟类有关的算数运算就会先把类转化成int,然后至于int跟任何数据进行算数、关系运算,是否会发生类型转化等等,就交给编译器了!下面我们看看程序是如何实现的:
class SamllInt{public://构造函数SamllInt (int i = 0 ):val(i){if(i <0 || i > 255)throw out_of_range("bad SmallInt initializer");}//转化函数:实现从本类型到int类型的转化operator int() const {return val;}private:std::size_t val;};
转换函数的基本格式是:operator type();其中type代表了你想转换成的类型,可以使内置的,也可以是自定义的(但是不能是viod或者数组,以及函数类型)。这个函数不指定返回值类型,也没有形参。这个函数必须声明为成员函数,而且因为转换时一般不改变成员的对象,所以通常声明为const。
我们不仅可以定义类类型到内置类型的转化,也可以定义类类型到类类型的转化:
class Integral{public://构造函数Integral(int i = 0):val(i){ }//转化函数:从本类转化成SmallInt类operator SamllInt() const {return val % 256 ;}private:size_t val;};
然后就能使用转化函数了:
int main(){SamllInt si(5);//经过类型转化后就能匹配函数的形参cout<<plus1(si)<<endl;//先将SamllInt转换成int,再使用内置转化来实现int到doublecout<<si-3.7<<endl;Integral intval;//使用Integral到SamllInt类的转化,然后调用默认的复制构造函数SamllInt si1(intval);//错误:没有直接的从Integral到int的转化int i = plus1(intval);return 0;}
注意,int i = plus1(intval);这句代码是错误的,因为虽然Integral 可以转化为SamllInt,SamllInt可以转化为int,但是不存在Integral直接到int的转化。
转化虽好,但也不能乱用。原因主要有两个:一是有时候这样的转化意义并不明确。比如对于一个Stduent类,我们完全没有必要去定义从这个类到的它的成员“学号”、“姓名”,“住址”的转化,因为这些成员很少与内置数据类型发生复杂的关系,比如大量的计算,比较等等。当使用它们时,通过get函数来获取可能更好一些。
第二大的原因就是大量的类型转化函数容易产生错误:转化的错误产生的原因很多,从大的角度分,可以分为由实参的匹配引起的,由重载引起的,以及混合表达引起的。下面对这几种情况分别举几个例子:
先看由实参匹配引起的:
int plus_i(int i){return i+1;}double plus_d(double i){return i+1;}long double plus_ld(long double i){return i +1;}using namespace std;int main(){SmallInt si(5);//使用operator int() const重载cout<<plus_i(si)<<endl;//使用operator double() const重载cout<<plus_d(si)<<endl;//二义性cout<<plus_ld(si)<<endl;return 0;}
让我们仔细分析一下这3次调用。对于第一次,使用的是使用operator int() const重载,这时实参与形参完全匹配。第二次有两种可能:1是使用使用operator double() const重载,实现实参到形参的匹配;2是使用operator int() const重载,将si转化为int,然后利用默认参数转化将int转化为duoble。但是直接匹配比需要转化要好,所以编译器选择了直接匹配。对于第三个函数,不论使用哪种类型转化,之后都要使用内置类型转化,没有一个比另一个更好,所以具有二义性。
实参与形参的匹配也会发生在单形参构造函数产生的隐式转化时。举个例子:
void manip(const SmallInt&){}int main(){double d = 0.0;int i = 0; long double s = 1;manip(d);manip(i);//二义性manip(s);return 0;}
还有一种情况,就是当两个定义了相互转化时,也可能存在二义性:
比如SmallInt类有一个接受Integral类实参的构造函数;而Integral类定义了转化到SmallInt类的操作。那么假如有一个函数的形参是SmallInt类,而你却填入了Integral的形参时,也会发生错误,因为此时既可以通过SmallInt类的构造函数将Integral转化为SmallInt类,然后调用该函数,也可以通过Integral类的转化函数将Integral类转化为SmallInt的转化函数转化为SmallInt类。
此时我们可以通过显示的指明到底使用哪种情况来避免这个问题。当然,最好是不要定义互相提供隐式转化的类。
再看由函数重载引起的:
void compute(int); void compute(double); void compute(long double);
假如我们的SmallInt只定义转换为int型的操作,那么判断使用哪个重载很简单:
SmallInt转化为int后,与第一个完全匹配;而对于后两个,还要进过标准转化,所以第一函数是最佳的可执行函数。
而不幸的是,我们的SmallInt还定义了到double的转化,此时,就会出现二义性;可以使用int转化调用第一个函数,也可以使用duoble转化调用第二个函数,此时它们都是完全匹配的。此时可以通过强制类型转化来指定使用哪个转化:
compute(static_cast<int>(si));
这种情况也会发生在构造函数的身上:
假如SmallInt类和Integral的构造函数都接受一个int的实参,而一个函数manip却定义了两个重载版本,接受的参数分别为SmallInt和Integral类。那么假如发生下面的调用:
manip(10);
就会出错,因为此时编译器会调用构造函数将10转化成manip需要的型参类型,而形参既可以是SmallInt类,也可以是Integral类,而这两个类恰好都有一个int型参数的构造函数,所以编译器不知道应该使用哪个。同理,此时可以通过显式构造函数来消除二义性:
manip(SmallInt(10)); manip(Integral(10));
最后看混合操作引起:
ClassX sc; int iobj = sc + 3;
假如sc定义了转化到int的操作,而ClassX类也重载了+操作,那么此时编译器就无法决定用哪种方式解决这个问题。由此可见如果定义了转化操作符,有定义了重载操作符,则十分容易引起二义性,所以有几点需要注意:
1.不要定义可以相互转化的类,不论是直接的转化还是通过构造函数。
2.不要定义接受内置算数类的转化。如果定义了,那么不要定义它们的重载版本;也不要定义了个以上的对算数类型的转化。
- 类型转化
- 类型转化
- 类型转化
- 类型转化
- 类型转化
- 类型相互转化
- 类型的转化
- c#.net 类型转化
- c++的类型转化
- c#.net 类型转化
- Struts 2 类型转化
- java类型转化
- oracle 类型转化函数
- vc中的类型转化
- c++中类型转化
- C++类型转化分析
- C++类型转化分析
- VC++类型转化
- 2012.7.9计划
- ZHUANJIA
- 正则表达式实战
- Windows Mobile引路蜂地图开发示例:二维图形库
- C#不让主线程UI挂起
- 类型转化
- 双循环链表的构建,插入,删除,输出
- 弹出窗口的js
- Android RoboGuice2 使用指南(3): Inject 自定义View
- 用doxygen生成面向对象语言的类文档
- Android ApiDemos示例解析(55):Graphics->BitmapMesh
- 无线网络-LTE (01) LTE Overview
- java读写文件大全
- 蒙迪欧致胜2.0T买油回来,怎么把汽油加进去?没有口啊?