在C中我们知道数据类型之间会进行隐式的类型安全转换,转换规则为小类型转大类型。
下面看一些隐式转换的例子:
short s = 'a';//合法
unsigned int ui = 200;//合法
int i = -1000;//合法
double d = i;//合法
上面语句都是合法的,要么同类型赋值,要么小类型往大类型赋值,当我们在进行数据类型运算时就要注意类型之间的隐式转换了。
if( (ui + i) > 0 )
cout << "(ui +i) > 0" << endl;
else
cout << "(ui +i) < 0" << endl;
这会输出(ui + i) < 0的语句,因为发生了类型转换,将i的类型提升到了unsigned int 了,所以两个数相加结果将是一个巨大的正数。
cout << "sizeof(s + 'b') = " << sizeof(s + 'b') << endl;
输出什么?将short类型和char类型相加后求类型长度,答案会是2吗?不是,由于编译器在计算int类型是最高效的,因为对于32位机子一次取32bit是最快的,也就是4字节,当编译器遇到一个short和一个char时就会先考虑将他们都转为int再进行运算
所以答案是4.
了解了普通类型之间的转换后来思考一下普通类型和类类型之间能发生转换吗?
定义一个Test类,里面有一个私有成员变量,一个无参构造函数和一个普通成员函数。
class Test
{
int mValue;
public:
Test()
{
mValue = 0;
}
};
执行下面语句:
Test t;
t = 5;
怎么样?能行吗?当然不行,编译器会报错,说两个操作数之间无法匹配。
此时是否就会想到通过直接调用构造函数生成一个临时对象后赋值给Test对象t。
那么我们加一个带参构造函数试试:
Test(int i)
{
mValue = i;
}
再执行上面语句就能通过编译了,为什么?
因为类里面有一种函数叫做转换构造函数,它本质还是构造函数,在需要进行隐式类型转换时调用,它满足只有一个参数,参数类型是普通类型或者是其他类类型(就是参数类型不能是自身类型)。
当有这个转换构造函数存在后,执行t = 5;时,由于在老旧的C版本中存在类似i = int(5);的行为,类似的,当直接对类对象进行赋非类对象类型的值时,编译器会查找当前类里面是否存在转换构造函数,如果存在,t = 5;就将隐式转成t = Test(5);然后调用构造函数,这是由于编译器会尽力帮你让源码通过编译,但这也不一定就是好事。
现在我们进行一个+操作符的重载:
Test operator + (const Test& p)
{
Test ret(mValue + p.mValue);
return ret;
}
执行这几条语句:
Test t;
t = 5;
Test r;
r = t + 10;
cout << r.value() << endl;
直接看结果吧,首先编译通过是肯定的,然后cout的结果是15,好像也没什么不对,结果就应该是15,为什么是15?因为对+操作符重载后可以在类类型之间进行+操作,编译器会将10隐式转换成Test(10),所以结果就为15.但是,r = t + 10;语句如果是我误操作呢?结果编译器却没有报错,这要是在工程中出现这种错误却并不提醒我显然是不友好的,所以这也是BUG的来源。
方法是有的,通过explicit关键字就可以拒绝编译器尝试进行隐式转换,当转换构造函数通过explicit关键字修饰后就只能进行显式转换。
static_cast <classname>(value);---->static_cast <Test> (10);或者Test(10);但是不推荐使用(Test)10;