15.9 用户定义的转换
1 class SmallInt{ 2 friend operator+(const SmallInt&,int); 3 friend operator-(const SmallInt&,int); 4 friend operator+(int,const SmallInt&); 5 friend operator-(int,const SmallInt&); 6 public: 7 Samll(int ival):value(ival){}; 8 operator+(const SmallInt&); 9 operator-(const SmallInt&); 10 //... 11 private: 12 int value; 13 }
定义6个操作,实现了SmallInt类与int型的运算,如:
SamllInt si(3);
si+3.14159
首先3.14159被转换成整型值3
调用operator+(const Small&,int),返回6,
但这并不是我们想要的结果,我们想得到6.174159
15.9.1 转换函数
C++提供一种机制,每个类可以定义一组“可被应用到该类型对象上的转换”,对于SmallInt队形,这里定义了该对象到int型的转换
1 class SmallInt{ 2 public: 3 SamllInt(int val):value(ival){} 4 //转换操作符 5 operator int(){return value;} 6 private: 7 int value; 8 }
SmallInt没有提供操作符重载
int()是一个转换函数,它定义了一个用户定义的转换
SmallInt si(3);
si+3.14159
则,调用SmallInt的转换函数,产生整形值3
整型值3被转换成3.0,得到6.14159
这样,所有int型能够执行的操作,si都能执行,如:si>127,将调用转换操作函数转成int
修改后的SmallInt
1 class SmallInt{ 2 friend istream& operator>>(istream & is,SmallInt& s); 3 friend ostream& operator<<(osstrea & os,const SmallInt &s) 4 {return os<<s.value;} 5 public: 6 SmallInt(int i=0):value(rangeCheck(i)){} 7 int operator=(int i){return (value=rangeCheck(i));} 8 operator int(){return value;} 9 private: 10 int rangeCheck(int); 11 int value; 12 }
1 istream& operator>>(istream &is,SmallInt &si) 2 { 3 int ix; 4 is>>ix; 5 si=ix; //调用SmallInt::operator=(int) 6 return is; 7 } 8 9 int SmallInt::rangeCheck(int i) 10 { 11 //... 12 }
转换函数不仅可以转换成内置类型,也可以是其他类型,如:
1 #inlcue "SmallInt.h” 2 typedef char* tName; 3 class Token{ 4 public: 5 Token(char*,int); 6 operator SmallInt(){return val;} 7 operator tName(){reurn name;} 8 operator int(){return val;} 9 private: 10 SmallInt val; 11 char* name; 12 } 13 14 Token t("aa",127); 15 int a=t;
这里调用Token::operator int()函数返回一个SmallInt对象,然后调用SmallInt::operator int()函数返回一个int型对象,所以Token的int转换函数直接返回SmallInt对象
转换函数标准格式:
(1) 转换函数必须是成员函数
(2) 以operator开头
(3) 声明不能指定返回类型和参数表
强制转换会调用转换函数,如:
1 #include "Token.h" 2 Token tok("fun",78); 3 //调用Token::operator SmallInt() 4 SmallInt tokval=SmllInt(tok); 5 //或者,执行隐式转换 6 SmallInt tokval2=tok; 7 //调用Token::operator tName() 8 char * tokName =static_cast<char*>(tok); 9 //或者,执行隐式转换 10 char* tokName2=tok;
Token::operator tName()转换函数提供了访问类私有成员的可能性,这样是我们不希望的,如:
*tokName='P';//这样我们修改了类的私有成员
我们可以重新定义tName
1 typedef const char* tName; 2 char *p=tok;//error,不允许把char*转换成const char* 3 const char *p2=tok//ok
第二种方法是改用string型,如:
1 class Token{ 2 public: 3 Token(string ,int); 4 operator SmallInt(){return val;} 5 operator string(){return name;} 6 operator int(){return val;} 7 //... 8 private: 9 SmallInt val; 10 string name; 11 };
采用以传值方式返回string,可以防止在类外修改私有成员的值,在执行构造函数的时候,name对象是重新获得的一块内存区域,对传进来的string也没有影响。
使用转换函数时,转换的目标类型必须与转换函数的类型完全匹配吗?如:
extern void calc(double);
Token tok("aaa",44);
calc(tok);//调用tok.operator int()函数,再执行int->double的标准转换
当使用用户定义的转换之后,只能允许使用标转转换序列,如果多次使用用户定义的转换,将是非法的。如:
extern void calc(int)
Token tok("aa",37);
calc(tok);//若没有定义Token::operator int(),则程序将报错
因为,calc(tok)将首先调用Token::operator SmallInt()将对象转换成SmallInt对象,再调用SmallInt::operator int()将Small对象转换成int型,这样就两次调用了用户定义的转换,程序将报错。
如果转换函数的类型与类类型之间没有逻辑匹配,则最好不要定义转换函数,如:
1 class Date{ 2 public: 3 operator int(); 4 private: 5 int month,day,year; 6 }
当调用int()时,却不知到该返回哪个值。
15.9.2 用构造函数作为转换函数
构造函数提供了把一种类型转换成另外一种类型的方式,
SmallInt的构造函数SmallInt(int) 提供了一种把int型转成SmallInt型的方法。如:
1 extern void calc(SmallInt); 2 int i; 3 calc(i);//编译器调用SmallInt(int)把i转成SmallInt对象 4 可以采用如下理解: 5 { 6 SmallInt temp=SmallInt(i); 7 calc(temp); 8 }
构造函数的参数也可以是另外一种类型,如:
class Number{ public: Number(const SmallInt&); } extern void func(Number); SmallInt si(8); func(si);//将调用Num
构造函数执行隐式转换是,构造函数的参数类型必须与被转换的值的类型完全匹配么?
extern void calc(SmallInt);
double dobj;
calc(dobj);//先执行标准转换,double->int,在构造函数int->SmallInt
我们可以在构造函数加explicit关键字来取消隐式转换,如:
class Number{ public: explicit Number(const SmallInt &); }; extern void func(Number); SmallInt si(87); int main() { func(si); //从SmallInt对象到Number对象没有隐式转换 }
不过我们可以:
func(Number(si)); //显示调用构造函数
func(static_cast<Number>(si));//强制转换