C
- 隐式类型转换
隐式类型转换在编译阶段完成,经常会有各种警告(编译器不一定能够猜测出我们的真实意图)。double d = 10.1; int i = d; // double 转为 int char *str = "hello world"; int *p = str; // char * 转为 int *
隐式类型转换存在风险。比如,double被转为int,存在数据精度的丢失(10.1变成了10);指针类型的转换前后同一个指针所能访问的范围是不一样的,比如char *
一次能够访 问的内存块是单个字节,而int *
一次能够访问的内存块是4字节,这就导致转换前后对同一个指针的使用可能出现非预期结果。
另外,对于隐式类型转,编译时换经常会有各种警告,改成显示类型转换我们可以去掉它们。 - 显示类型转换
显示类型转换直接在要转换的成员前指明需要转换成的类型,简单粗暴;此时编译器不会给出警告,由此产生的潜在安全问题需要程序员自行考量。使用显式类型转换时,你要知道你在做什么。C++可使用这种显示类型转换。double d = 10.1; int i = (int)d; char *str = "hello world"; int *p = (int *)str;
C++
-
reinterpret_cast
格式:reinterpret_cast<type>(expression)double d = 10.1; int i = d; // double转为int char *str = "hello world"; int *p = reinterpret_cast<int *>(str); // char * 转为 int * printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
- type必须是一个指针、引用、算术类型、函数指针或者成员指针
- 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针
- 跟C风格的显示类型转换类似,没有安全性检查
注:无法更改const等属性
-
const_cast
上面的例子第三行会有警告
warning: deprecated conversion from string constant to ‘char*’
原因是字符串(“hello world”)默认是常量类型–只读,而
char *
默认是可读写的,所以会有冲突警告。消除警告的方法是前面添加const
关键字声明char *
为只读。另外,C++显示类型转换reinterpret_cast只能更改数据类型而无法更改读写属性,若要将
const char *
转为int *
需要使用类型转换 const_cast:格式:const_cast<type>(expression)
该运算符用来去除原来类型的const或volatile属性。
除了const 或volatile修饰之外, type和expression的类型是一样的。double d = 10.1; int i = d; // double转为int const char *str = "hello world"; char *str2 = const_cast<char *>(str); // 去掉 const 属性 int *p = reinterpret_cast<int *>(str2); // char *转为int * printf("i = %d, str = 0x%x, p = 0x%x\n", i, reinterpret_cast<unsigned int>(str), reinterpret_cast<unsigned int>(p));
从C++类型转换的格式可看出,C++的类型转换其实是方法(函数),<>表示该函数为模板函数。
-
dynamic_cast
常常有多个子类继承自同一个父类,当使用父类引用/指针等访问子类对象时我们需要知道其属于具体哪个子类,此时要用到动态类型转换 dynamic_cast,格式:
dynamic_cast<type>(expression)#include <iostream> #include <string.h> #include <unistd.h> using namespace std; class Human { public: virtual void eating(void) { cout<<"use hand to eat"<<endl; } virtual ~Human() { cout<<"~Human()"<<endl; } }; class English : public Human { public: void eating(void) { cout<<"use knife to eat"<<endl; } virtual ~English() { cout<<"~English()"<<endl; } }; class Chinese : public Human { public: void eating(void) { cout<<"use chopsticks to eat"<<endl; } virtual ~Chinese() { cout<<"~Chinese()"<<endl; } }; void test_eating(Human& h) { English *pe; Chinese *pc; h.eating(); /* Is this person English or Chinese? */ if (pe = dynamic_cast<English *>(&h)) cout<<"This human is English"<<endl; if (pc = dynamic_cast<Chinese *>(&h)) cout<<"This human is Chinese"<<endl; } int main(int argc, char **argv) { Human h; English e; Chinese c; test_eating(h); test_eating(e); test_eating(c); return 0; }
动态类型转换只能用在含虚函数的类中。动态类型转换的机制:含虚函数的类的实例化对象中有一个指针,该指针指向一块包含虚函数表和类信息(含继承关系)的内容,从中可获知对象属于哪个类。类信息包含本类信息,还包含类的继承关系。比如,多重继承的时候,动态类型转换也能分辨出来:
class JXman: public Chinese { public: void eating(void) { cout<<"use chopsticks to eat, I come from JX"<<endl; } }; void test_eating(Human& h) { English *pe; Chinese *pc; JXman *pj; h.eating(); /* Is this person English or Chinese or JXman? */ if (pe = dynamic_cast<English *>(&h)) cout<<"This human is English"<<endl; if (pc = dynamic_cast<Chinese *>(&h)) cout<<"This human is Chinese"<<endl; if (pg = dynamic_cast<JXman *>(&h)) cout<<"This human is JXman "<<endl; } int main(int argc, char **argv) { JXman j; test_eating(j); return 0; }
上面会输出This human is Chinese和This human is JXman两条信息。
值得注意的是,动态类型转换中推荐使用指针,而不是引用。
void test_eating(Human& h) { English& pe = dynamic_cast<English&>(h); //如果一个引用不存在实体,就没有存在的必要了。这里会导致程序执行崩溃(编译没问题)。 Chinese& pc = dynamic_cast<Chinese&>(h); JXman& pj = dynamic_cast<JXman&>(h); h.eating(); } int main(int argc, char **argv) { JXman j; test_eating(j); return 0; }
动态类型转换有可能成功也有可能失败,失败时程序会异常终止,而强制类型转换reinterpret_cast可能会引入风险,所以dynamic_cast比reinterpret_cast更安全。例如:
#if 0 English& pe = dynamic_cast<English&>(h); //如果一个引用不存在实体,就没有存在的必要了。这里会导致程序执行崩溃(编译没问题)。 Chinese& pc = dynamic_cast<Chinese&>(h); JXman& pj = dynamic_cast<JXman&>(h); #else English& pe = reinterpret_cast<English&>(h); //同样编译没问题,也能正常执行,但是有隐藏风险! Chinese& pc = reinterpret_cast<Chinese&>(h); JXman& pj = reinterpret_cast<JXman&>(h); #endif
总结一下:
-
dynamic_cast把expression转换成type类型的对象;type必须是类的指针、类的引用或者void *。如果type是类指针类型,那么expression也必须是一个指针;如果type是一个引用,那么expression也必须是一个引用。
-
用于多态场合,即:必须有虚函数
-
主要用于类层次间的上行转换(子类对象转换为父类对象)和下行转换(父类对象转换为子类对象),还可以用于类之间的交叉转换
-
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
-
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
-
-
static_cast
格式:static_cast<type>(expression)。静态类型转换在程序编译阶段进行,没有运行时类型检查来保证转换的安全性。int main(int argc, char **argv) { Human h; //English e; //Chinese c; JXman j; English *pe; pe = static_cast<English *>(&h);//编译正常。但下行转换有风险(Human不一定是English),但static_cast检测不出来。 English *pe2 = static_cast<English *>(&j);//编译报错。 Chinese *pc = static_cast<Chinese *>(&j);//编译正常,执行正常。有继承关系的向上转换。 return 0; }
有如下特征:
-
用于类层次结构中基类和子类之间指针或引用的转换。
-
进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
-
进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的,需要程序要自行确保程序逻辑的正确性。
-
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum:这种转换的安全性也要开发人员来保证。
-
把void指针转换成目标类型的指针(不安全!!)
-
把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
-
参考:韦东山C++教程