简言
在C++中有时候两种类型需要相互转换,有可能是隐式转换也有可能是显示转换。隐式类型转换由编译器决定,能转就转。显示转换(强转)需要由用户自己决定。
隐式转换
double num = 3.14;
int sum = num + 5;
例如上面的例子,num是一个double型的数据,5是一个int型的数据,而sum是一个int型的数据。那么编译器遇到这种情况会怎么办呢?C++不允许两个不同类型的值相加,而是会先根据类型转换规则将运算对象的类型转换一致后再相加。上述的类型转换是自动执行的,所以称为隐式转换。隐式转换应该尽可能避免精度的损失,当算术运算表达式中既有整型类型的运算对象,又有浮点型类型的运算对象时,会将整形运算对象的类型转换为浮点型。所以上述的例子中,当double类型的num加上int类型的5时,编译器会将int型5转换为double类型的5相加得到double型结果8.14;然后用double型值8.14去初始化int型对象sum,这里会丢弃到小数点后的部分,用8去初始化对象sum。
什么时候会发生隐式转换呢?往往在下面的几种情况会发生隐式转换:
1、在条件中,非布尔值会转换成布尔值;
2、在初始化过程中,初始值转化为变量的类型;
3、在赋值语句中,右侧运算对象转换成左侧运算对象的值;
4、如果算术运算或关系运算的运算对象有多个类型时,需要转换成同一种类型。
强制转换
C++提供了四种强制转换的方式,分别是static_cast,dynamic_cast,const_ccast和reinterpret_cast四种。强制转换的形式为:cast-name<type>(expression),其中cast-name为四种强制转换方式的一种,type为将要转换的类型,expression是要转换的值。
static_cast
void test01()
{
double num1 = 12.5;
int num2 = num1; //隐式转换,编译器会警告提醒精度损失
int num3 = (int)num1; //C风格强制转换,不会提醒精度损失
int num4 = static_cast<int>(num1); //C++风格强制转换,不会提醒精度损失
}
void test02(void *p)
{
int a = 10;
double *ptr = static_cast<double*>(p); //将void*指针转为double*类型指针
int *p1 = &a;
double *ptrD = static_cast<double*>(p1); //不同类型的指针不能强转
}
static_cast可用于隐式执行的任何转换,还可以用于转换指针,我们知道void *指针是可以指向任何指针类型的,但是相反不可以,我们就可以用static_cast强制转换成我们想要的指针类型。但是不同类型的指针之间是不可以直接转换的。还有static_cast不能去掉对象的底层const属性。什么是底层const呢?顶层const:顶层const表示指针本身是个常量,底层const:底层const表示指针所指的对象是个常量。
const_cast
const_cast只能改变对象的底层const。
const char *p;
char *ptr = const_cast<char*>(p);
将常量对象去掉常量性质,被称为去掉const性质。当我们去掉const性质后,就可以对该对象进行写操作了。
dynamic_cast
dynamic_cast是一个运行时类型识别运算符,程序在运行时确定对象的类型。dynamic_cast用于将一个对象的指针或引用转换为另一个对象的指针或引用。一般是将父类对象的指针或引用转化为子类对象的指针或引用,反之也可将子类对象的指针或引用转化为父类对象的指针或引用(本身就可以,不需要强转)。
所以一般使用格式就是:派生类指针 = dynamic_cast<派生类类型*>(基类指针);如果转换失败了就返回0。注意:dynamic_cast只适用于包含虚函数的类,dynamic_cast是为多态而准备的,dynamic_cast代码示例:
class Animal
{
public:
virtual int getAge() const { return m_age; }
virtual void show() { std::cout << "hello world!"; } //基类show函数
protected:
int m_age;
};
class Cat :public Animal
{
public:
virtual void show() { std::cout << "I am is a Cat!"; } //派生类show函数
void func() { std::cout << "I am so cute!"; } //func函数不是虚函数!!!
};
int main()
{
Animal* animal = new Cat();
animal->show();
if (Cat* cat = dynamic_cast<Cat*>(animal))
{
cat->func();
}
else
{
//强转失败
}
}
一般当我们使用基类指针指向一个子类对象时,可以通过虚函数重写来调用子类的虚函数。但我们使用dynamic_cast强制转换后将基类指针转换为派生类指针,就可以调用派生类的非虚函数func();
reinterpret_cast
reinterpret_cast的意思是重新解释,它可以用于不同类型的指针之间转换,static_cast不可以。reinterpret_cast<type>(expression)中的type和expression的类型中必须有一个为引用或者是指针。同样reinterpret_cast不能改变const属性。
int *p;
char* d= reinterpret_cast<char*>(p)
总结
最后的结尾引用C++经典书籍对于强制转换的总结。强制类型转换干扰了正常的类型检查,因此我们强烈建议程序员避免使用强制类型转换。这个建议对于reinterpret cast 尤其适用,因为此类类型转换总是充满了风险。其他强制类型转换,比如 static cast 和dynamic cast,都不应该频繁使用。每次书写了一条强制类型转换语句,都应该反复斟酌能否以其他方式实现相同的目标。就算实在无法避免,也应该尽量限制类型转换值的作用域,并且记录对相关类型的所有假定,这样可以减少错误发生的机会。(C++Primer 5th)