c++四种显式类型转换
1.显式转换
命名的强制类型转换:一个命名的强制类型转换具有以下形式:
cast-name<type>(expression)
其中type是转换的目标类型,expression是要转换的值,如果type是引用类型,则结果是左值,cast-name是static_cast,dynamic_cast,const_cast和reinterpret_cast中的一种。其中dynamic_cast支持运行时类型识别。cast-name说明了是哪种类型转换。
(1) static_cast
任何一个具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast,例如,通过一个整型运算对象强制转换成doulbe类型就可以使表达式执行浮点数除法。
一般来说,当编译器发现将一个较大的算数类型试图赋值给较小的类型,就会给出警告信息,但是如果执行静态类型转换,警告就会消失。
当我们把指针存放在void*中,并且使用static_cast将其强制类型转换回原来的类型时,应该确保指针的值保持不变,强制类型转换的结果和原始的地址值相等,类型一旦不符,就会产生未定义的结果。
(2) const_cast
const_cast只能改变运算对象底层const,对于将常量对象转换成非常量对象的行为,一般称为去掉const性质(cast away the const),一般去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行读写操作了,如果对象本身不是一个常量,使用强制类型转换获取写权限是合法行为。如果对象是一个常量,再使用const_cast进行写操作就会产生其它未定义的结果。
只有const_cast能够改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。
对于基本数据类型char float int double等通过引用的方式修改常量的值,再通过常量名来访问是无效的,g++编译器会对该常量进行优化,代码中的变量名其实直接被替换成常量的原始值,实际上变量名代表的常量是被修改的,只能通过指针和引用的方式可以得到修改的值。
const int it = 2000;
int& itk = const_cast<int&>(it);
itk = 1000;
const int* itp = ⁢
const int& itr = it;
std::cout << "itp: " << *itp << " itr: " << itr << " it: " << it << std::endl
// itp: 1000 itr: 1000 it: 2000
对于类类型和结构体类型则直接可以得到修改后的值。
const std::string st = "not modify";
std::string& mi = const_cast<std::string&>(st);
mi = "is modify";
std::cout << "st: " << st << " mi: " << mi << std::endl;
// st: is modify mi: is modify
对于const char*对象是一个常量,对常量进行写操作会引发异常。
const char* m = "wangjing";
char* n = const_cast<char*>(m);
n[0] = 's'; // Exception thrown: write access violation
(3) reinterpret_cast
reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。假设有如下转换
int* ip;
char *pc =reinterpret_cast<char*>(ip);
我们必须要牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。例如:
std::string str(pc);
pc此时不是以’\0’结尾的字符串,string在初始化时调用strlen函数,该函数根据’\0’的位置计算pc的长度,当pc是int类型时,则不满足该条件,不能使用pc对str进行初始化。
使用reinterpret_cast是十分危险的,本质上依赖于机器,如果要安全的使用reinterpret_cast,必须要对涉及的类型和编译器实现转换的过程有清除的了解。
(4) dynamic_cast
dynamic_cast使用形式如下所示:
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
其中type必须是一个类类型,通常情况下该类型具有虚函数,第一种形式中,e必须是一个有效的指针,第二种形式中,e必须是一个左值,第三种形式中,e不能是左值。
在上面所有的形式中,e的类型必须符合以下三个条件中的任意一个:e的类型是目标type的公有派生类,e的类型是目标type的公有基类,或者e的类型就是目标type的类型。如果符合,则类型转换可以成功,否则,转换失败。如果一条dynamic_cast语句的转换目标失败了,则结果为0。如果转换目标是引用类型且失败了,则dynamic_cast运算符抛出一个bad_cast的异常。
注意:
1、dynamic_cast在将父类cast到子类时,父类必须要有虚函数,否则编译器会报错。
2、 dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行(子类转换成父类)转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换(父类转换成子类)时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class Parent {
public:
virtual void pr() {
std::cout << "this is parent." << std::endl;
}
};
class Child: public Parent {
public:
int age;
void pr() {
std::cout << "this is child." << std::endl;
}
};
int main(int argc, char* argv[]) {
Child child;
child.age = 20;
Parent parent;
// 上行转换,子类转换成父类
Child* childP = &child;
Parent* parentP = dynamic_cast<Parent*>(childP);
parentP->pr();
// 下行转换, parentP指向实际的子类对象,注意应当使用标准做法。
parentP = &child;
childP = dynamic_cast<Child*>(parentP);
childP->pr();
// 下行转换,parentP指向父类的对象,为不安全操作,返回空指针
parentP = &parent;
childP = dynamic_cast<Child *>(parentP);
if (NULL != childP) // 标准做法
{
childP->pr();
}
else {
std::cout << "fail to convert." << std::endl;
}
// 使用static_cast不安全
parentP = &parent;
childP = static_cast<Child*>(parentP);
childP->pr();
std::cout << childP->age << std::endl; // 程序可以运行,但是为不安全操作
return 0;
}
// 输出
this is child
this is child
fail to convert
this is parent
-858993460