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));//强制转换

原文地址:https://www.cnblogs.com/estival/p/3242474.html