相比较于内置类型的转换,类类型的转换可能要更复杂一些,先从隐式转换说起。
类类型的隐式转换最直接的就是通过构造函数来隐式转换,当某个函数希望调用的实参是类类型的时候,程序却赋值其他内置类型的实参,那么编译器将自动查找该赋值类型是否有构造函数与之对应。
比如:
class hcm
{
public:
hcm(const string &love="")
: ability(10000), capital("much"), affection(love), {}
hcm(istream &is);
};
调用hcm类中的函数hcm.item(hcm a); 时传入的实参为:
string cy("hakuna,matata");
hcm.item(cy); 则将字符串类型cy通过构造函数隐式转换为类类型hcm,但是这种转换根据我们的需求不同,很可能是危险的,会使得程序出现我们没有想到的bug,所以程序员需要良好的编程习惯,看是否需要这种转换,如果不需要的话需要及时的屏蔽。
在类定义内部(只能是在类定义内部,如果是在外部定义时,因为还没到类的作用域所以没办法达到效果)调用关键字explicit,可以屏蔽这种隐式转换。
class hcm
{
public:
explicit hcm(const string &love="")
: ability(10000), capital("much"), affection(love), {}
explicit hcm(istream &is);
};对于利用构造函数的类类型转换,还可以显式的调用构造函数来进行转换。
hcm.itrm(hcm(cy));
①转换的用处:
1.支持混合类型的表达式(利用转换可以减少重载函数的个数)
2.减少所需重载操作符的数目。(定义类类型转换成内置类型,可以无需重载任何操作符)
②转换操作符:
是一类特殊的类成员函数,它没有返回类型,在类定义体内声明,在保留字operator之后跟着转换的目标类型:
class hcm
{
public:
operator int() { return val; }//必须返回一个与期望类型相同类型的值
private:
size_t val;
};
不论是内置类型、类类型、指针、引用还是由类型别名定义的名字,都可以用作目标类型。但是不能转换为数组类型和函数类型。、
还要注意的是只应用一个类类型的转换,就是说假如类hcm可以转换为int类型,而chq可以转换为hcm类型,那么在需要调用int类型实参的函数中不能期望调用chq->hcm->int,只能做到第一步转换。
那么,如果定义多个转换操作符呢?
如果有实参的完全匹配,当然编译器会直接采用完全匹配,但是在没有完全匹配的情况下,又有多个转换操作符则必须采用标准转换就会产生二义性。
类似于多个转换操作符的情况,多个构造函数也会出现这种二义性。
对于两个类类型,如果定义了两种类型的相互转换,很可能产生二义性。
比如:
class hcm;
class chq;
{
public:
chq(hcm);
};
class hcm
{
public:
operator chq() const;
};
void compute(chq);
hcm a;
compute(a);
hcm类型对象a转换为chq类型时,不论是利用构造函数还是转换操作符都没有优先性,所以会产生二义性。(都是直接转换)这种情况下不能用显式转换操作来解决二义性,因为显示转换的时候也不知道是利用构造函数来转换还是利用转换操作符来转换,所以我们需要的是利用显式的调用构造函数或者调用转换操作符来解决。
当有多个转换和重载的时候,编译器总是会报错,因为编译器不会区别多种转换的转换类型等级了,例如:
class hcm
{
public:
operator int() const {return val};
operator double() const {return val};
private:
size_t val;
};
void compute(int);
void compute(double);
void compute(long double);
hcm a;
compute(a);
如此只能强制利用显式转换使得消除二义性。
compute(static_cast(a)); //编译器将会直接采用int转换操作符